Compare commits
	
		
			66 Commits
		
	
	
		
			b71bbe2dc5
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 47c3e98dbf | |||
| 6a1cd0eab5 | |||
| 701d9a9cc0 | |||
| 2cca684908 | |||
| d8e5d6eedc | |||
| 19f5c70735 | |||
| 10d82cf81a | |||
| deaf2f111a | |||
| c9464b1605 | |||
| 1404a0432a | |||
| 5d96031992 | |||
| 2209145e74 | |||
| cfbc12da3b | |||
| fd906a574b | |||
| 628eb8eaa6 | |||
| 8c146d24f6 | |||
| 908100514f | |||
| 8d9ecf6935 | |||
| f0544779bb | |||
| 382e12671b | |||
| 14abc3c2b4 | |||
| 8753e3e103 | |||
| a280194079 | |||
| cd3f4471cc | |||
| 66f00aa7b5 | |||
| ca6e7ad778 | |||
| 20f9a4485e | |||
| d250e136c3 | |||
| 9a2f9a6564 | |||
| 067c9055d2 | |||
| 652947b30a | |||
| c526284d98 | |||
| 51f2e986a9 | |||
| 3e259a530c | |||
| 79aa32e8e4 | |||
| b97d729ef1 | |||
| 09fafa59d4 | |||
| 86d6d2dadd | |||
| 98a2c94083 | |||
| e6f85ff014 | |||
| 86ec25a499 | |||
| 5e5b9274e1 | |||
| b3f0e85574 | |||
| 5649294be0 | |||
| 8cee9dc5bf | |||
| d0641950f9 | |||
| d91460fa82 | |||
| fec638a5c8 | |||
| 0e827053de | |||
| c456ef301c | |||
| d7c809af82 | |||
| d77c4e2478 | |||
| 93fc535eaf | |||
| bc1e38c327 | |||
| ebeecccd63 | |||
| 12d973d785 | |||
| 9672b804e6 | |||
| 2a266eea23 | |||
| ca3ebf9f9b | |||
| 2433dbb447 | |||
| 2a4804ecb2 | |||
| 6fab39bf86 | |||
| 666d0895e4 | |||
| 52172812cb | |||
| ca518af9e8 | |||
| 001b8218bd | 
							
								
								
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| local_settings.py | ||||
| .git | ||||
| .gitignore | ||||
| githubtoken.txt | ||||
							
								
								
									
										133
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										133
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Executable 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
									
								
							
							
						
						
									
										20
									
								
								Dockerfile.dev
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										37
									
								
								Dockerfile.local
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										23
									
								
								Dockerfile.prod
									
									
									
									
									
										Normal 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"] | ||||
							
								
								
									
										116
									
								
								app/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										116
									
								
								app/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -10,12 +10,10 @@ from flask_login import LoginManager | ||||
| from sqlalchemy.orm import sessionmaker | ||||
| from werkzeug.routing import BaseConverter | ||||
| from flask_mail import Mail | ||||
| from config import load_config | ||||
| import re  | ||||
|  | ||||
| try: | ||||
|     from local_settings import ApplicationConfig | ||||
| except Exception as e: | ||||
|     from settings import ApplicationConfig | ||||
|  | ||||
| ApplicationConfig = load_config() | ||||
|  | ||||
| app = Flask(__name__, | ||||
|             static_url_path='', | ||||
| @@ -27,8 +25,7 @@ app.config.from_object(ApplicationConfig) | ||||
|  | ||||
| session = sessionmaker() | ||||
|  | ||||
| check_enviroment = ApplicationConfig.CURRENT_SETTINGS | ||||
| print(f"starting server with {check_enviroment} settings") | ||||
|  | ||||
|  | ||||
|  | ||||
| class RegexConverter(BaseConverter): | ||||
| @@ -42,10 +39,8 @@ app.jinja_env.autoescape = True | ||||
|  | ||||
|  | ||||
| # 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_ALLOW'] = ApplicationConfig.UPLOADED_FILES_ALLOW | ||||
| 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_PERMANENT'] = ApplicationConfig.SESSION_PERMANENT | ||||
| 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['SECRET_KEY'] = ApplicationConfig.SECRET_KEY | ||||
|  | ||||
| session.configure(bind=ApplicationConfig.SQLALCHEMY_DATABASE_URI) | ||||
| db = SQLAlchemy(app) | ||||
| bcrypt = Bcrypt(app) | ||||
| app.config['SESSION_SQLALCHEMY'] = db | ||||
| server_session = Session(app) | ||||
| ma = Marshmallow(app) | ||||
| mail = Mail(app) | ||||
| @@ -75,39 +70,41 @@ login_manager.anonymous_user = "Guest" | ||||
| @login_manager.request_loader | ||||
| def load_user_from_request(request): | ||||
|     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') | ||||
|     if api_key: | ||||
|         user = db.session\ | ||||
|             .query(Auth_User)\ | ||||
|             .filter_by(api_key=api_key)\ | ||||
|             .first() | ||||
|         if user: | ||||
|             return user | ||||
|     # next, try to log in using Basic Auth | ||||
|     api_key_auth = request.headers.get('Authorization') | ||||
|     if api_key_auth: | ||||
|         api_key = api_key_auth.replace('bearer ', '', 1) | ||||
|         if api_key.startswith('"') and api_key.endswith('"'): | ||||
|             api_key = api_key[1:-1] | ||||
|         user = db.session\ | ||||
|             .query(Auth_User)\ | ||||
|             .filter_by(api_key=api_key)\ | ||||
|             .first() | ||||
|      | ||||
|     # 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: | ||||
|             user = db.session.query(Auth_User).filter_by(api_key=api_key).first() | ||||
|             if user: | ||||
|                 return user | ||||
|  | ||||
|     # As a fallback, check for api_key in URL args (less secure, but keeps existing logic) | ||||
|     api_key_arg = request.args.get('api_key') | ||||
|     if api_key_arg: | ||||
|         user = db.session.query(Auth_User).filter_by(api_key=api_key_arg).first() | ||||
|         if user: | ||||
|             return user | ||||
|              | ||||
|     # If no valid key is found in header or args, return None | ||||
|     return None | ||||
|  | ||||
|  | ||||
| api_main = { | ||||
|     "origins": [ApplicationConfig.ORIGIN_URL], | ||||
|     "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"], | ||||
|     "allow_headers": ['Authorization', 'application/json', 'authorization', 'Content-Type', | ||||
|                       'Access-Control-Allow-Headers', 'Origin,Accept', | ||||
|                       'X-Requested-With', 'Content-Type', 'Access-Control-Request-Method',  | ||||
|                       'Access-Control-Request-Headers'] | ||||
| } | ||||
| cors = CORS(app,  supports_credentials=True, resources={r'/*': api_main}) | ||||
| # api_main = { | ||||
| #     "origins": [ApplicationConfig.ORIGIN_URL], | ||||
| #     "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"], | ||||
| #     "allow_headers": ['Authorization', 'application/json', 'authorization', 'Content-Type', | ||||
| #                       'Access-Control-Allow-Headers', 'Origin,Accept', | ||||
| #                       'X-Requested-With', 'Content-Type', 'Access-Control-Request-cMethod',  | ||||
| #                       'Access-Control-Request-Headers'] | ||||
| # } | ||||
| cors = CORS(app,  | ||||
|              supports_credentials=True, | ||||
|              resources={r"/*": {"origins": ApplicationConfig.CORS_ALLOWED_ORIGINS} | ||||
|                         }) | ||||
|  | ||||
|  | ||||
| # bind a function after each request, even if an exception is encountered. | ||||
| @@ -122,42 +119,42 @@ def teardown_appcontext(error): | ||||
|  | ||||
|  | ||||
| @app.errorhandler(500) | ||||
| def internal_error500(): | ||||
| def internal_error500(error): | ||||
|     return jsonify({"error": "Internal Error 500"}), 500 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(502) | ||||
| def internal_error502(): | ||||
| def internal_error502(error): | ||||
|     return jsonify({"error": "Internal Error 502"}), 502 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(404) | ||||
| def internal_error404(): | ||||
| def internal_error404(error): | ||||
|     return jsonify({"error": "Internal Error 400"}), 400 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(401) | ||||
| def internal_error404(): | ||||
| def internal_error401(error): | ||||
|     return jsonify({"error": "Internal Error 401"}), 401 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(400) | ||||
| def internal_error400(): | ||||
| def internal_error400(error): | ||||
|     return jsonify({"error": "Internal Error 400"}), 400 | ||||
|  | ||||
|  | ||||
| @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 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(403) | ||||
| def internal_error403(): | ||||
| def internal_error403(error): | ||||
|     return jsonify({"error": "Internal Error 403"}), 403 | ||||
|  | ||||
|  | ||||
| @app.errorhandler(405) | ||||
| def internal_error(): | ||||
| def internal_error(error): | ||||
|     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 | ||||
| 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 | ||||
| app.register_blueprint(delivery_blueprint, url_prefix='/delivery') | ||||
|  | ||||
| from .delivery_data import delivery_data as delivery_data_blueprint | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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(): | ||||
|    | ||||
|     db.configure_mappers() | ||||
|     db.create_all() | ||||
|     db.session.commit() | ||||
|   | ||||
							
								
								
									
										0
									
								
								app/admin/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/admin/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										87
									
								
								app/admin/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										87
									
								
								app/admin/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -3,11 +3,10 @@ from flask_login import current_user, logout_user, login_user, login_required | ||||
| from app.admin import admin | ||||
| from app import db | ||||
| from datetime import datetime | ||||
| from app.classes.pricing import (Pricing_Service_General, | ||||
| from app.classes.pricing import ( | ||||
|                                  Pricing_Oil_Oil, | ||||
|                                  Pricing_Service_General_schema, | ||||
|                                  Pricing_Oil_Oil_schema) | ||||
|  | ||||
| from app.classes.admin import Admin_Company, Admin_Company_schema, Call | ||||
|  | ||||
| @admin.route("/oil/create", methods=["POST"]) | ||||
| def create_oil_price(): | ||||
| @@ -18,13 +17,27 @@ def create_oil_price(): | ||||
|     price_from_supplier = request.json["price_from_supplier"] | ||||
|     price_for_customer = request.json["price_for_customer"] | ||||
|     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( | ||||
|         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, | ||||
|         price_emergency=price_emergency, | ||||
|         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.commit() | ||||
| @@ -35,49 +48,6 @@ def create_oil_price(): | ||||
|     }), 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"]) | ||||
| def get_oil_price(): | ||||
| @@ -90,3 +60,28 @@ def get_oil_price(): | ||||
|                       .first()) | ||||
|     price_schema = Pricing_Oil_Oil_schema(many=False) | ||||
|     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
									
								
							
							
						
						
									
										0
									
								
								app/auth/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										171
									
								
								app/auth/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										171
									
								
								app/auth/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,47 +1,45 @@ | ||||
| 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 import db, bcrypt | ||||
| from datetime import datetime | ||||
| from uuid import uuid4 | ||||
| from app.classes.auth import Auth_User | ||||
|  | ||||
| from app.classes.employee import Employee_Employee  | ||||
| import re | ||||
|  | ||||
| @auth.route("/whoami", methods=["GET"]) | ||||
| 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') | ||||
|     if not api_key: | ||||
|         return jsonify({"error": "True"}), 200 | ||||
|     else: | ||||
|         api_key = api_key.replace('bearer ', '', 1) | ||||
|         api_key = api_key.replace('"', '') | ||||
|         user_exists = (db.session | ||||
|                           .query(Auth_User) | ||||
|                           .filter(Auth_User.api_key == api_key) | ||||
|                           .first()) | ||||
|         if not user_exists: | ||||
|             return jsonify({"error": True}), 200 | ||||
|         else: | ||||
|             user = db.session\ | ||||
|                 .query(Auth_User)\ | ||||
|                 .filter(Auth_User.api_key == api_key)\ | ||||
|                 .first() | ||||
|             return jsonify({ | ||||
|                 "ok": True, | ||||
|                 'user': { | ||||
|                     'user_name': user.username, | ||||
|                     'user_id': user.id, | ||||
|                     'user_email': user.email, | ||||
|                     'user_admin': user.admin_role, | ||||
|                     'token': user.api_key, | ||||
|                     'confirmed': user.confirmed | ||||
|                          }, | ||||
|                 'token': user.api_key | ||||
|             }), 200 | ||||
|     # --- THIS IS THE FIX --- | ||||
|     # Use a case-insensitive regular expression to remove "bearer " | ||||
|     # This handles "Bearer ", "bearer ", "BEARER ", etc. | ||||
|     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: | ||||
|         print("no user found with that api key") | ||||
|         return jsonify({"ok": False, "error": "Invalid token"}), 401 | ||||
|  | ||||
|     # Now, build the complete response with both user and employee data. | ||||
|     return jsonify({ | ||||
|         "ok": True, | ||||
|         'user': { | ||||
|             'user_name': user.username, | ||||
|             'user_id': user.id, | ||||
|             'user_email': user.email, | ||||
|             'user_admin': user.admin_role, | ||||
|             'token': user.api_key, | ||||
|             'confirmed': user.confirmed | ||||
|         }, | ||||
|     }), 200 | ||||
|  | ||||
|  | ||||
| @auth.route("/amiconfirmed", methods=["GET"]) | ||||
| @@ -76,46 +74,31 @@ def logout(): | ||||
|  | ||||
| @auth.route("/login", methods=["POST"]) | ||||
| def login(): | ||||
|     """ | ||||
|     Main post function to  a user | ||||
|     """ | ||||
|     | ||||
|     username = request.json["username"] | ||||
|     password = request.json["password"] | ||||
|  | ||||
|     user = db.session\ | ||||
|                 .query(Auth_User)\ | ||||
|                 .filter_by(username=username)\ | ||||
|                 .first() is not None | ||||
|     user = db.session.query(Auth_User).filter_by(username=username).first() | ||||
|  | ||||
|     # Important checks! | ||||
|     if not user: | ||||
|         return jsonify({"error": True}), 200 | ||||
|     user = db.session\ | ||||
|         .query(Auth_User)\ | ||||
|         .filter_by(username=username)\ | ||||
|         .first() | ||||
|         return jsonify({"error": "User not found"}), 401 # Use a more descriptive error and status code | ||||
|  | ||||
|     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) | ||||
|         new_fails = current_fails + 1 | ||||
|         user.fails = new_fails | ||||
|         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() | ||||
|  | ||||
|     # Check if user is active | ||||
|     if user.active != 1: | ||||
|         return jsonify({"error": "Please contact a manager. Login rejected"}), 401 | ||||
|  | ||||
|     # If login is successful, return the correct structure | ||||
|     return jsonify({ | ||||
|         "ok": True, | ||||
|         'user': {'user_id': user.uuid, | ||||
|                     'user_id': user.id, | ||||
|                     'user_email': user.email, | ||||
|                     'admin_role': user.admin_role, | ||||
|                     'token': user.api_key | ||||
|                     }, | ||||
|         'user': { | ||||
|             'user_name': user.username, | ||||
|             'user_id': user.id, | ||||
|             'user_email': user.email, | ||||
|             'admin_role': user.admin_role, | ||||
|         }, | ||||
|         'token': user.api_key | ||||
|     }), 200 | ||||
|  | ||||
| @@ -189,17 +172,21 @@ def register_user(): | ||||
|  | ||||
|  | ||||
| @auth.route('/change-password', methods=['POST']) | ||||
| @login_required | ||||
| 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_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): | ||||
|         return jsonify({"error": "Error: Incorrect Passwords"}), 200 | ||||
|  | ||||
| @@ -211,5 +198,49 @@ def change_password(): | ||||
|      | ||||
|     db.session.add(user) | ||||
|     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
									
								
							
							
						
						
									
										0
									
								
								app/classes/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										9
									
								
								app/classes/admin.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										9
									
								
								app/classes/admin.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -4,7 +4,6 @@ from datetime import datetime | ||||
|  | ||||
| class Admin_Company(db.Model): | ||||
|     __tablename__ = 'admin_company' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|  | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -24,3 +23,11 @@ class Admin_Company_schema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         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
									
								
							
							
						
						
									
										4
									
								
								app/classes/auth.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -10,7 +10,6 @@ def get_uuid(): | ||||
|  | ||||
| class Auth_User(UserMixin, db.Model): | ||||
|     __tablename__ = 'auth_users' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|  | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -27,6 +26,7 @@ class Auth_User(UserMixin, db.Model): | ||||
|     admin = db.Column(db.INTEGER) | ||||
|     admin_role = db.Column(db.INTEGER) | ||||
|     confirmed = db.Column(db.INTEGER) | ||||
|     active = db.Column(db.INTEGER, default=1) | ||||
|  | ||||
|     def __init__(self, | ||||
|                  username, | ||||
| @@ -38,6 +38,7 @@ class Auth_User(UserMixin, db.Model): | ||||
|                  admin, | ||||
|                  admin_role, | ||||
|                  confirmed, | ||||
|                  active=1, | ||||
|                  ): | ||||
|         self.username = username | ||||
|         self.api_key = api_key | ||||
| @@ -48,6 +49,7 @@ class Auth_User(UserMixin, db.Model): | ||||
|         self.admin = admin | ||||
|         self.admin_role = admin_role | ||||
|         self.confirmed = confirmed | ||||
|         self.active = active | ||||
|  | ||||
|     def is_authenticated(self): | ||||
|         return True | ||||
|   | ||||
							
								
								
									
										82
									
								
								app/classes/auto.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										82
									
								
								app/classes/auto.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -2,21 +2,31 @@ | ||||
| from app import db, ma | ||||
| 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): | ||||
|     __tablename__ = 'auto_temp' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
|                    primary_key=True, | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|     todays_date = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) | ||||
|     temp = db.Column(db.DECIMAL(50, 2)) | ||||
|     temp_max = db.Column(db.DECIMAL(50, 2)) | ||||
|     temp_min = db.Column(db.DECIMAL(50, 2)) | ||||
|     temp_avg = db.Column(db.DECIMAL(50, 2)) | ||||
|     todays_date = db.Column(db.DATE()) | ||||
|     temp = db.Column(db.DECIMAL(6, 2)) | ||||
|     temp_max = db.Column(db.DECIMAL(6, 2)) | ||||
|     temp_min = db.Column(db.DECIMAL(6, 2)) | ||||
|     temp_avg = db.Column(db.DECIMAL(6, 2)) | ||||
|     degree_day = db.Column(db.INTEGER()) | ||||
|  | ||||
| class Auto_Temp_schema(ma.SQLAlchemyAutoSchema): | ||||
| @@ -27,7 +37,6 @@ class Auto_Temp_schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Auto_Delivery(db.Model): | ||||
|     __tablename__ = 'auto_delivery' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -35,15 +44,62 @@ class Auto_Delivery(db.Model): | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|     customer_id =  db.Column(db.INTEGER()) | ||||
|     customer_full_name = db.Column(db.DECIMAL(50, 2)) | ||||
|     last_fill = db.Column(db.TIMESTAMP()) | ||||
|     last_updated = db.Column(db.TIMESTAMP()) | ||||
|     estimated_gallons_left = db.Column(db.INTEGER()) | ||||
|     estimated_gallons_left_prev_day = 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)) | ||||
|     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_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 Meta: | ||||
|         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
									
								
							
							
						
						
									
										9
									
								
								app/classes/cards.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -5,7 +5,6 @@ from datetime import datetime | ||||
|  | ||||
| class Card_Card(db.Model): | ||||
|     __tablename__ = 'card_card' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -17,12 +16,14 @@ class Card_Card(db.Model): | ||||
|     card_number = db.Column(db.VARCHAR(50)) | ||||
|     last_four_digits = db.Column(db.INTEGER()) | ||||
|     name_on_card = db.Column(db.VARCHAR(500)) | ||||
|     expiration_month = db.Column(db.INTEGER()) | ||||
|     expiration_year = db.Column(db.INTEGER()) | ||||
|     expiration_month = db.Column(db.VARCHAR(20)) | ||||
|     expiration_year = db.Column(db.VARCHAR(20)) | ||||
|     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()) | ||||
|     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 Meta: | ||||
|   | ||||
							
								
								
									
										5
									
								
								app/classes/company.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										5
									
								
								app/classes/company.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -4,7 +4,6 @@ from app import db, ma | ||||
|  | ||||
| class Company_Company(db.Model): | ||||
|     __tablename__ = 'company_company' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -15,8 +14,8 @@ class Company_Company(db.Model): | ||||
|     company_dba_name = db.Column(db.VARCHAR(250)) | ||||
|     company_llc_name = db.Column(db.VARCHAR(250)) | ||||
|     company_town = db.Column(db.VARCHAR(140)) | ||||
|     company_state = db.Column(db.VARCHAR(140)) | ||||
|     company_zip = db.Column(db.INTEGER) | ||||
|     company_state = db.Column(db.INTEGER) | ||||
|     company_zip = db.Column(db.VARCHAR(25)) | ||||
|  | ||||
|  | ||||
| class Company_Company_schema(ma.SQLAlchemyAutoSchema): | ||||
|   | ||||
							
								
								
									
										66
									
								
								app/classes/customer.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										66
									
								
								app/classes/customer.py
									
									
									
									
									
										
										
										Normal file → Executable 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): | ||||
|     __tablename__ = 'customer_customer' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
|                    primary_key=True, | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|     auth_net_profile_id = db.Column(db.String, unique=True, index=True, nullable=True) | ||||
|     account_number = db.Column(db.VARCHAR(25)) | ||||
|     customer_last_name = db.Column(db.VARCHAR(250)) | ||||
|     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_first_call = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) | ||||
|     customer_first_call = db.Column(db.TIMESTAMP()) | ||||
|     customer_email = db.Column(db.VARCHAR(500)) | ||||
|     customer_automatic = db.Column(db.INTEGER) | ||||
|     customer_phone_number = db.Column(db.VARCHAR(25)) | ||||
|     customer_home_type = db.Column(db.INTEGER) | ||||
|     customer_apt = db.Column(db.VARCHAR(140)) | ||||
|     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 Meta: | ||||
| @@ -34,7 +37,6 @@ class Customer_Customer_schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Customer_Property(db.Model): | ||||
|     __tablename__ = 'customer_property' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -60,7 +62,6 @@ class Customer_Property_schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Customer_Payment_Credit(db.Model): | ||||
|     __tablename__ = 'customer_payment' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     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_number = 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 Meta: | ||||
|         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 | ||||
|          | ||||
|          | ||||
							
								
								
									
										25
									
								
								app/classes/customer_social.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/classes/customer_social.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										70
									
								
								app/classes/delivery.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,10 +1,10 @@ | ||||
|  | ||||
| from app import db, ma | ||||
| from datetime import datetime | ||||
|  | ||||
|  | ||||
| class Delivery_Delivery(db.Model): | ||||
|     __tablename__ = 'delivery_delivery' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -23,81 +23,73 @@ class Delivery_Delivery(db.Model): | ||||
|     # if customer asked for a fill | ||||
|     customer_asked_for_fill = db.Column(db.INTEGER) | ||||
|     # 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 | ||||
|     customer_filled = db.Column(db.INTEGER) | ||||
|     # integer value if delivered, waiting, cancelled etc | ||||
|     # waiting = 0 | ||||
|     # delivered = 1 | ||||
|     # cancelled = 1 | ||||
|     # out for delivery = 2 | ||||
|     # cancelled = 3 | ||||
|     # partial delivery = 4 | ||||
|     # tommorrow = 3 | ||||
|     # issue = 5 | ||||
|  | ||||
|     # finalized = 10 | ||||
|  | ||||
|     delivery_status = db.Column(db.INTEGER) | ||||
|  | ||||
|     # 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_delivered = db.Column(db.TIMESTAMP(), default=None) | ||||
|     when_delivered = db.Column(db.DATE(), default=None) | ||||
|     # when the delivery is expected ie what day | ||||
|     expected_delivery_date = db.Column(db.DATE(), default=None) | ||||
|     # automatic delivery | ||||
|     automatic = db.Column(db.INTEGER) | ||||
|     automatic_id = db.Column(db.INTEGER) | ||||
|     # OIL info and id from table | ||||
|     oil_id = db.Column(db.INTEGER) | ||||
|     supplier_price = db.Column(db.DECIMAL(50, 2)) | ||||
|     customer_price = db.Column(db.DECIMAL(50, 2)) | ||||
|     supplier_price = db.Column(db.DECIMAL(6, 2)) | ||||
|     customer_price = db.Column(db.DECIMAL(6, 2)) | ||||
|     # weather | ||||
|     customer_temperature = db.Column(db.DECIMAL(50, 2)) | ||||
|     # services | ||||
|     customer_temperature = db.Column(db.DECIMAL(6, 2)) | ||||
|   | ||||
|     dispatcher_notes = db.Column(db.TEXT()) | ||||
|  | ||||
|  | ||||
|     prime = db.Column(db.INTEGER) | ||||
|     same_day = db.Column(db.INTEGER) | ||||
|     emergency = db.Column(db.INTEGER) | ||||
|      | ||||
|     # cash = 0 | ||||
|     # credit = 1 | ||||
|     # credit/cash = 2 | ||||
|     # check = 3 | ||||
|     # other = 4 | ||||
|     payment_type = 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_last_name = db.Column(db.VARCHAR(140)) | ||||
|  | ||||
|     pre_charge_amount = db.Column(db.DECIMAL(50, 2)) | ||||
|     total_price = db.Column(db.DECIMAL(50, 2)) | ||||
|     final_price = db.Column(db.DECIMAL(50, 2)) | ||||
|     pre_charge_amount = db.Column(db.DECIMAL(6, 2)) | ||||
|     total_price = db.Column(db.DECIMAL(6, 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 Meta: | ||||
|         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): | ||||
|     __tablename__ = 'delivery_notes' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
|   | ||||
							
								
								
									
										5
									
								
								app/classes/employee.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										5
									
								
								app/classes/employee.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -4,7 +4,6 @@ from datetime import datetime | ||||
|  | ||||
| class Employee_Employee(db.Model): | ||||
|     __tablename__ = 'employee_employee' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -34,7 +33,6 @@ class Employee_Employee_schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Employee_Credentials(db.Model): | ||||
|     __tablename__ = 'employee_credentials' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -54,7 +52,6 @@ class Employee_Credentials_schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Employee_Vacation(db.Model): | ||||
|     __tablename__ = 'employee_vacation' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -64,7 +61,7 @@ class Employee_Vacation(db.Model): | ||||
|     employee_id = db.Column(db.INTEGER) | ||||
|     employee_name = db.Column(db.VARCHAR(140)) | ||||
|     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) | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										46
									
								
								app/classes/money.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								app/classes/money.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										32
									
								
								app/classes/pricing.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -2,34 +2,11 @@ from app import db, ma, login_manager | ||||
| 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): | ||||
|     __tablename__ = 'pricing_oil_oil' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|  | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -37,9 +14,12 @@ class Pricing_Oil_Oil(db.Model): | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|  | ||||
|     price_from_supplier = db.Column(db.DECIMAL(50, 2)) | ||||
|     price_for_customer = db.Column(db.DECIMAL(50, 2)) | ||||
|     price_for_employee = db.Column(db.DECIMAL(50, 2)) | ||||
|     price_from_supplier = db.Column(db.DECIMAL(6, 2)) | ||||
|     price_for_customer = db.Column(db.DECIMAL(6, 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()) | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										26
									
								
								app/classes/promo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								app/classes/promo.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										23
									
								
								app/classes/query.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -4,7 +4,6 @@ from app import db, ma | ||||
|  | ||||
| class Query_EmployeeTypeList(db.Model): | ||||
|     __tablename__ = 'query_employee_type_list' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|     id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||||
|     value = db.Column(db.INTEGER) | ||||
| @@ -20,13 +19,14 @@ class Query_EmployeeTypeList_Schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Query_StateList(db.Model): | ||||
|     __tablename__ = 'query_state_list' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __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_StateList_Schema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = Query_StateList | ||||
| @@ -35,9 +35,24 @@ class Query_StateList_Schema(ma.SQLAlchemyAutoSchema): | ||||
|     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): | ||||
|     __tablename__ = 'query_customer_type_list' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|     id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||||
|     value = db.Column(db.INTEGER) | ||||
| @@ -55,7 +70,6 @@ class Query_CustomerTypeList_Schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Query_ServiceTypeList(db.Model): | ||||
|     __tablename__ = 'query_service_type_list' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|     id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||||
|     value = db.Column(db.INTEGER) | ||||
| @@ -73,7 +87,6 @@ class Query_ServiceTypeList_Schema(ma.SQLAlchemyAutoSchema): | ||||
|  | ||||
| class Query_DeliveryStatusList(db.Model): | ||||
|     __tablename__ = 'query_delivery_type_list' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|     id = db.Column(db.Integer, primary_key=True, autoincrement=True) | ||||
|     value = db.Column(db.INTEGER) | ||||
|   | ||||
| @@ -3,9 +3,8 @@ from app import db, ma | ||||
| from datetime import datetime | ||||
|  | ||||
|  | ||||
| class Service_Call(db.Model): | ||||
|     __tablename__ = 'service_call' | ||||
|     __bind_key__ = 'eamco' | ||||
| class Service_Service(db.Model): | ||||
|     __tablename__ = 'service_service' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -14,52 +13,38 @@ class Service_Call(db.Model): | ||||
|                    unique=False) | ||||
|      | ||||
|     customer_id = db.Column(db.INTEGER) | ||||
|     customer_last_name = db.Column(db.VARCHAR(250)) | ||||
|     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_name = 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 | ||||
|     #1 = open | ||||
|     status = db.Column(db.INTEGER) | ||||
|     service_cost = db.Column(db.Numeric(10, 2), nullable=False) | ||||
|      | ||||
|     # 0 = unknown | ||||
|     # 1 = cleaning / tuneup | ||||
|     # 2 = problem | ||||
|     # 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) | ||||
|     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 Service_Call_schema(ma.SQLAlchemyAutoSchema): | ||||
| class Service_Service_schema(ma.SQLAlchemyAutoSchema): | ||||
|     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' | ||||
|     __bind_key__ = 'eamco' | ||||
|  | ||||
| class Service_Parts(db.Model): | ||||
|     __tablename__ = 'service_parts' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -67,20 +52,24 @@ class Service_Call_Money(db.Model): | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|      | ||||
|     service_call_id = db.Column(db.INTEGER) | ||||
|     hours = db.Column(db.DECIMAL(50, 2)) | ||||
|     cost_per_hour = db.Column(db.DECIMAL(50, 2)) | ||||
|     parts_cost = db.Column(db.DECIMAL(50, 2)) | ||||
|     customer_id = db.Column(db.INTEGER) | ||||
|     oil_filter = db.Column(db.VARCHAR(100)) | ||||
|     oil_filter_2 = db.Column(db.VARCHAR(100)) | ||||
|     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: | ||||
|         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' | ||||
|     __bind_key__ = 'eamco' | ||||
|  | ||||
| class Service_Plans(db.Model): | ||||
|     __tablename__ = 'service_plans' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|  | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -88,39 +77,13 @@ class Service_Call_Notes_Dispatcher(db.Model): | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|  | ||||
|     service_call_id = db.Column(db.INTEGER) | ||||
|     dispatcher_notes = db.Column(db.TEXT) | ||||
|     dispatcher_subject = db.Column(db.VARCHAR(1024)) | ||||
|     time_added = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) | ||||
|     dispatcher_id = db.Column(db.INTEGER) | ||||
|     dispatcher_name = db.Column(db.VARCHAR(140)) | ||||
|     customer_id = db.Column(db.INTEGER) | ||||
|     contract_plan = db.Column(db.INTEGER, default=0)  # 0=no contract, 1=standard, 2=premium | ||||
|     contract_years = db.Column(db.INTEGER, default=1) | ||||
|     contract_start_date = db.Column(db.DATE()) | ||||
|  | ||||
|  | ||||
| class Service_Call_Notes_Dispatcher_schema(ma.SQLAlchemyAutoSchema): | ||||
| class Service_Plans_schema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = Service_Call_Notes_Dispatcher | ||||
|  | ||||
|  | ||||
| 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 | ||||
|  | ||||
|  | ||||
|  | ||||
|         model = Service_Plans | ||||
|     contract_start_date = ma.DateTime(format='%Y-%m-%d') | ||||
|   | ||||
							
								
								
									
										20
									
								
								app/classes/stats_company.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/classes/stats_company.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										16
									
								
								app/classes/stats_customer.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -5,25 +5,27 @@ from datetime import datetime | ||||
|  | ||||
| class Stats_Customer(db.Model): | ||||
|     __tablename__ = 'stats_customer' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
|                    primary_key=True, | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|      | ||||
|     customer_id = db.Column(db.INTEGER) | ||||
|     total_calls = db.Column(db.INTEGER) | ||||
|     service_calls_total = db.Column(db.INTEGER) | ||||
|     service_calls_total_spent = db.Column(db.DECIMAL(50, 2)) | ||||
|     service_calls_total_profit = db.Column(db.DECIMAL(50, 2)) | ||||
|     service_calls_total_spent = db.Column(db.DECIMAL(6, 2)) | ||||
|     service_calls_total_profit = db.Column(db.DECIMAL(6, 2)) | ||||
|      | ||||
|      | ||||
|     oil_deliveries = db.Column(db.INTEGER) | ||||
|     oil_total_gallons = db.Column(db.INTEGER) | ||||
|     oil_total_spent = db.Column(db.DECIMAL(50, 2)) | ||||
|     oil_total_profit = db.Column(db.DECIMAL(50, 2)) | ||||
|     oil_total_gallons = db.Column(db.DECIMAL(6, 2)) | ||||
|     oil_total_spent = db.Column(db.DECIMAL(6, 2)) | ||||
|     oil_total_profit = db.Column(db.DECIMAL(6, 2)) | ||||
|      | ||||
| class Stats_Customer_schema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = Stats_Customer | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										31
									
								
								app/classes/stats_employee.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										31
									
								
								app/classes/stats_employee.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,23 +1,22 @@ | ||||
|  | ||||
| from app import db, ma, login_manager | ||||
| from app import db, ma | ||||
| from datetime import datetime | ||||
|  | ||||
|  | ||||
| class Stats_Employee_Oil(db.Model): | ||||
|     __tablename__ = 'stats_employee_oil' | ||||
|     __bind_key__ = 'eamco' | ||||
|     __table_args__ = {"schema": "public"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
|                    primary_key=True, | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|      | ||||
|     employee_id = db.Column(db.INTEGER) | ||||
|     total_deliveries = db.Column(db.INTEGER) | ||||
|     total_gallons_delivered = 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(50, 2)) | ||||
|     oil_total_profit_delivered = db.Column(db.DECIMAL(6, 2)) | ||||
|     oil_total_money_delivered = db.Column(db.DECIMAL(6, 2)) | ||||
|      | ||||
| class Stats_Employee_Oil_schema(ma.SQLAlchemyAutoSchema): | ||||
|     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"} | ||||
|      | ||||
|     id = db.Column(db.Integer, | ||||
| @@ -36,11 +36,12 @@ class Stats_Employee_Service(db.Model): | ||||
|                    autoincrement=True, | ||||
|                    unique=False) | ||||
|      | ||||
|     total_service_calls = db.Column(db.INTEGER) | ||||
|     total_service_calls_hours = db.Column(db.INTEGER) | ||||
|     total_gallons_fuel = 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_Service_schema(ma.SQLAlchemyAutoSchema): | ||||
|     employee_id = db.Column(db.INTEGER) | ||||
|     total_orders = db.Column(db.INTEGER) | ||||
|     total_credit_cards_proccessed = db.Column(db.INTEGER) | ||||
|      | ||||
| class Stats_Employee_Office_schema(ma.SQLAlchemyAutoSchema): | ||||
|     class Meta: | ||||
|         model = Stats_Employee_Service | ||||
|         model = Stats_Employee_Office | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
							
								
								
									
										22
									
								
								app/classes/transactions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/classes/transactions.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										0
									
								
								app/common/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								app/common/decorators.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/common/decorators.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								app/customer/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/customer/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										478
									
								
								app/customer/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										478
									
								
								app/customer/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,18 +1,39 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import login_required | ||||
| from geopy.geocoders import Nominatim | ||||
| from app.customer import customer | ||||
| from app import db | ||||
| from datetime import datetime | ||||
| from app.classes.cards import Card_Card | ||||
| from app.classes.customer import \ | ||||
|     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.auto import Auto_Delivery,Auto_Delivery_schema | ||||
| from app.classes.stats_customer import Stats_Customer | ||||
| import string | ||||
| 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"]) | ||||
| @login_required | ||||
|  | ||||
| def all_customers_around(): | ||||
|     customer_list = db.session \ | ||||
|         .query(Customer_Customer) \ | ||||
| @@ -22,13 +43,13 @@ def all_customers_around(): | ||||
|  | ||||
|  | ||||
| @customer.route("/all/<int:page>", methods=["GET"]) | ||||
| @login_required | ||||
|  | ||||
| def all_customers(page): | ||||
|     """ | ||||
|     pagination all customers | ||||
|     """ | ||||
|  | ||||
|     per_page_amount = 50 | ||||
|     per_page_amount = 100 | ||||
|     if page is None: | ||||
|         offset_limit = 0 | ||||
|     elif page == 1: | ||||
| @@ -38,6 +59,7 @@ def all_customers(page): | ||||
|  | ||||
|     customer_list = db.session \ | ||||
|         .query(Customer_Customer) \ | ||||
|         .order_by(Customer_Customer.id.desc())  \ | ||||
|         .limit(per_page_amount).offset(offset_limit) | ||||
|  | ||||
|     customer_schema = Customer_Customer_schema(many=True) | ||||
| @@ -47,30 +69,113 @@ def all_customers(page): | ||||
| @customer.route("/<int:customer_id>", methods=["GET"]) | ||||
| def get_a_customer(customer_id): | ||||
|     """ | ||||
|     Checks auth token to ensure user is authenticated | ||||
|     """ | ||||
|     get_customer = (db.session | ||||
|         .query(Customer_Customer) | ||||
|         .filter(Customer_Customer.id == customer_id) | ||||
|         .first()) | ||||
|     print(get_customer) | ||||
|     customer_schema = Customer_Customer_schema(many=False) | ||||
|     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"]) | ||||
| @login_required | ||||
|  | ||||
| def create_customer(): | ||||
|     """ | ||||
|     """ | ||||
|     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_first_name = request.json["customer_first_name"] | ||||
| @@ -78,20 +183,77 @@ def create_customer(): | ||||
|     response_customer_state = request.json["customer_state"] | ||||
|     response_customer_zip = request.json["customer_zip"] | ||||
|     response_customer_email = request.json["customer_email"] | ||||
|     response_customer_automatic = request.json["customer_automatic"] | ||||
|     response_customer_home_type = request.json["customer_home_type"] | ||||
|     customer_phone_number = request.json["customer_phone_number"] | ||||
|     customer_address = request.json["customer_address"] | ||||
|     customer_apt = request.json["customer_apt"] | ||||
|     if response_customer_automatic is True: | ||||
|         auto_customer = 1 | ||||
|     else: | ||||
|         auto_customer = 0 | ||||
|     customer_description_msg = request.json["customer_description"] | ||||
|      | ||||
|  | ||||
|     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) | ||||
|  | ||||
|  | ||||
|     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( | ||||
|         account_number=made_account_number, | ||||
|         customer_last_name=response_customer_last_name, | ||||
| @@ -101,15 +263,51 @@ def create_customer(): | ||||
|         customer_zip=response_customer_zip, | ||||
|         customer_first_call=now, | ||||
|         customer_email=response_customer_email, | ||||
|         customer_automatic=auto_customer, | ||||
|         customer_automatic=0, | ||||
|         customer_home_type=int_customer_home_type, | ||||
|         customer_phone_number=customer_phone_number, | ||||
|         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.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() | ||||
|  | ||||
|     return jsonify({ | ||||
| @@ -122,6 +320,7 @@ def create_customer(): | ||||
|     }), 200 | ||||
|  | ||||
|  | ||||
|  | ||||
| @customer.route("/edit/<int:customer_id>", methods=["PUT"]) | ||||
| @login_required | ||||
| def edit_customer(customer_id): | ||||
| @@ -131,6 +330,10 @@ def edit_customer(customer_id): | ||||
|                     .query(Customer_Customer) | ||||
|                     .filter(Customer_Customer.id == customer_id) | ||||
|                     .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_first_name = request.json["customer_first_name"] | ||||
|     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_phone_number = request.json["customer_phone_number"] | ||||
|     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_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_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_last_name = response_customer_last_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_zip = response_customer_zip | ||||
|     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.commit() | ||||
| @@ -186,7 +422,6 @@ def delete_customer(customer_id): | ||||
|  | ||||
|     db.session.delete(get_customer) | ||||
|     db.session.commit() | ||||
|     print("deleted") | ||||
|     return jsonify({ | ||||
|         "ok": True, | ||||
|         'user': { | ||||
| @@ -196,3 +431,196 @@ def delete_customer(customer_id): | ||||
|     }), 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
									
								
							
							
						
						
									
										0
									
								
								app/delivery/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										753
									
								
								app/delivery/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										753
									
								
								app/delivery/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								app/delivery_data/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/delivery_data/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										207
									
								
								app/delivery_data/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										207
									
								
								app/delivery_data/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,123 +1,160 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import current_user | ||||
| from datetime import date | ||||
| from datetime import datetime | ||||
| from decimal import Decimal | ||||
| from app.delivery_data import delivery_data | ||||
| from app import db | ||||
| from datetime import datetime | ||||
| from app.classes.customer import Customer_Customer | ||||
| from app.classes.delivery import (Delivery_Delivery, | ||||
|                                   Delivery_Delivery_schema, | ||||
|                                   Delivery_Notes_Driver, | ||||
|                                   Delivery_Payment, | ||||
|                                   Delivery_Payment_schema, | ||||
|                                   ) | ||||
| from app.classes.customer import Customer_Customer, Customer_Description | ||||
| from app.classes.delivery import Delivery_Delivery | ||||
| from app.classes.employee import Employee_Employee | ||||
| from app.classes.cards import Card_Card | ||||
| from app.classes.pricing import Pricing_Oil_Oil | ||||
| from app.classes.auth import Auth_User | ||||
| from app.classes.pricing import Pricing_Service_General | ||||
|  | ||||
|  | ||||
|  | ||||
| @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)) | ||||
|  | ||||
|  | ||||
| from app.classes.stats_employee import Stats_Employee_Oil | ||||
| from app.classes.auto import Auto_Delivery | ||||
| from app.classes.stats_customer import Stats_Customer | ||||
|  | ||||
| @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 | ||||
|     """ | ||||
|     now = datetime.utcnow() | ||||
|     get_delivery = db.session \ | ||||
|         .query(Delivery_Delivery) \ | ||||
|         .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() | ||||
|      | ||||
|     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"] | ||||
|     card_payment = request.json["credit"] | ||||
|     cash_payment = request.json["cash"] | ||||
|  | ||||
|     if request.json["credit_card_id"]: | ||||
|         card_payment_id = request.json["credit_card_id"] | ||||
|     else: | ||||
|         card_payment_id = None | ||||
|     check_number = request.json["check_number"] | ||||
|     fill_location = request.json["fill_location"] | ||||
|  | ||||
|  | ||||
|     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 | ||||
|  | ||||
|  | ||||
|  | ||||
|     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 driver  | ||||
|     get_delivery.driver_last_name = get_driver.employee_last_name | ||||
|     get_delivery.driver_first_name = get_driver.employee_first_name | ||||
|     get_delivery.driver_employee_id = get_driver.id | ||||
|  | ||||
|     # update delivery | ||||
|     get_delivery.when_delivered = now | ||||
|     get_delivery.gallons_delivered = gallons_delivered | ||||
|     get_delivery.prime = prime_info | ||||
|     get_delivery.same_day = same_day_info | ||||
|     get_delivery.payment_type = delivery_payment_method | ||||
|     get_delivery.payment_card_id = card_id_from_customer | ||||
|  | ||||
|     get_delivery.cash_recieved = cash_amount | ||||
|     get_delivery.check_number = check_number | ||||
|     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.commit() | ||||
|  | ||||
|  | ||||
|     return jsonify({ | ||||
|         "ok": True, | ||||
|         'delivery': { | ||||
|             'id': get_delivery.id, | ||||
|         }, | ||||
|     }), 200 | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								app/delivery_status/__init__.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								app/delivery_status/__init__.py
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										67
									
								
								app/delivery_status/views.py
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										0
									
								
								app/employees/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										63
									
								
								app/employees/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										63
									
								
								app/employees/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,20 +1,39 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import current_user | ||||
|  | ||||
| from sqlalchemy import or_ | ||||
| from datetime import date, timedelta | ||||
|  | ||||
| from flask_login import login_required | ||||
| from app.employees import employees | ||||
| from app import db | ||||
| from datetime import datetime | ||||
| 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"]) | ||||
| @login_required | ||||
| def get_specific_employee(userid): | ||||
|     employee = db.session \ | ||||
|         .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() | ||||
|     employee_schema = Employee_Employee_schema(many=False) | ||||
|     return jsonify(employee_schema.dump(employee)) | ||||
| @@ -27,7 +46,6 @@ def get_specific_employee_user_id(userid): | ||||
|         .query(Employee_Employee) \ | ||||
|         .filter(Employee_Employee.user_id == userid) \ | ||||
|         .first() | ||||
|     # print(employee.id) | ||||
|     employee_schema = Employee_Employee_schema(many=False) | ||||
|     return jsonify(employee_schema.dump(employee)) | ||||
|  | ||||
| @@ -69,9 +87,11 @@ def all_employees(): | ||||
| def all_employees_drivers(): | ||||
|     employee_list = db.session \ | ||||
|         .query(Employee_Employee) \ | ||||
|         .filter(Employee_Employee.employee_type == 4) \ | ||||
|         .filter(or_(Employee_Employee.employee_type == 4, | ||||
|                     Employee_Employee.employee_type == 8)) \ | ||||
|         .all() | ||||
|     customer_schema = Employee_Employee_schema(mwany=True) | ||||
|  | ||||
|     customer_schema = Employee_Employee_schema(many=True) | ||||
|     return jsonify(customer_schema.dump(employee_list)) | ||||
|  | ||||
|  | ||||
| @@ -130,8 +150,25 @@ def employee_create(): | ||||
|         employee_phone_number=e_phone_number, | ||||
|     ) | ||||
|  | ||||
|  | ||||
|     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, | ||||
|                     'user_id': new_employee.id, | ||||
| @@ -152,7 +189,7 @@ def employee_edit(employee_id): | ||||
|     e_type = request.json["employee_type"] | ||||
|     e_start_date = request.json["employee_start_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 \ | ||||
|         .query(Employee_Employee) \ | ||||
| @@ -170,6 +207,12 @@ def employee_edit(employee_id): | ||||
|     if e_end_date != 'None': | ||||
|         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.commit() | ||||
|  | ||||
|   | ||||
							
								
								
									
										0
									
								
								app/info/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/info/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										62
									
								
								app/info/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										62
									
								
								app/info/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,37 +1,65 @@ | ||||
| from flask import jsonify | ||||
| from decimal import Decimal | ||||
| from app.info import info | ||||
| 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.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"]) | ||||
| def get_oil_price(): | ||||
| def get_oil_price_today(): | ||||
|     get_price_query = (db.session | ||||
|                      .query(Pricing_Oil_Oil) | ||||
|                      .order_by(Pricing_Oil_Oil.date.desc()) | ||||
|                      .first()) | ||||
|     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 | ||||
|  | ||||
|  | ||||
| @info.route("/price/service", methods=["GET"]) | ||||
| def get_service_price(): | ||||
| @info.route("/price/oil/table", methods=["GET"]) | ||||
| def get_pricing(): | ||||
|     get_price_query = (db.session | ||||
|                          .query(Pricing_Service_General) | ||||
|                          .order_by(Pricing_Service_General.date.desc()) | ||||
|                          .first()) | ||||
|                      .query(Pricing_Oil_Oil) | ||||
|                      .order_by(Pricing_Oil_Oil.date.desc()) | ||||
|                      .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"]) | ||||
|   | ||||
							
								
								
									
										0
									
								
								app/main/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/main/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										6
									
								
								app/main/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										6
									
								
								app/main/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,6 +1,10 @@ | ||||
| from flask import jsonify, Response | ||||
| from flask import jsonify, Response, url_for | ||||
| from app import app | ||||
|  | ||||
| @app.route("/favicon.ico") | ||||
| def favicon(): | ||||
|     return url_for('static', filename='data:,') | ||||
|  | ||||
|  | ||||
| @app.route('/robots.txt') | ||||
| @app.route('/sitemap.xml') | ||||
|   | ||||
							
								
								
									
										8
									
								
								app/money/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/money/__init__.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										83
									
								
								app/money/views.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										0
									
								
								app/payment/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										580
									
								
								app/payment/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										580
									
								
								app/payment/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -3,32 +3,51 @@ from app.payment import payment | ||||
| from app import db | ||||
| from app.classes.customer import Customer_Customer | ||||
| 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 | ||||
|     """ | ||||
|     get_card_count = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.user_id == user_id) \ | ||||
|     get_card_count = ( | ||||
|          db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.user_id == user_id)  | ||||
|         .count() | ||||
|     print(get_card_count) | ||||
|     if get_card_count > 0: | ||||
|         print("true") | ||||
|         ) | ||||
|      | ||||
|         get_old_card = db.session \ | ||||
|             .query(Card_Card) \ | ||||
|             .filter(Card_Card.main_card == True) \ | ||||
|             .filter(Card_Card.user_id == user_id) \ | ||||
|     get_card = ( | ||||
|          db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.user_id == user_id)  | ||||
|         .filter(Card_Card.id == card_id)  | ||||
|         .first() | ||||
|         ) | ||||
|  | ||||
|     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() | ||||
|         print(get_old_card.id) | ||||
|         ) | ||||
|  | ||||
|         get_old_card.main_card = False | ||||
|         get_card.main_card = True | ||||
|  | ||||
|         db.session.add(get_old_card) | ||||
|         db.session.commit() | ||||
|     else: | ||||
|  | ||||
|  | ||||
|         get_card.main_card = True | ||||
|  | ||||
|         db.session.add(get_card) | ||||
|         db.session.commit() | ||||
|  | ||||
|  | ||||
| @@ -37,10 +56,10 @@ def get_user_cards(user_id): | ||||
|     """ | ||||
|     gets all cards of a user | ||||
|     """ | ||||
|     get_u_cards = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.user_id == user_id) \ | ||||
|         .all() | ||||
|     get_u_cards = (db.session | ||||
|         .query(Card_Card) | ||||
|         .filter(Card_Card.user_id == user_id) | ||||
|         .all()) | ||||
|  | ||||
|     card_schema = Card_Card_schema(many=True) | ||||
|     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 | ||||
|     """ | ||||
|  | ||||
|     get_u_cards = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.user_id == user_id) \ | ||||
|         .count() | ||||
|     get_u_cards = (db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.user_id == user_id)  | ||||
|         .count()) | ||||
|  | ||||
|     return jsonify({ | ||||
|         "ok": True, | ||||
| @@ -69,56 +88,15 @@ def get_user_specific_card(card_id): | ||||
|     gets a specific card of a user | ||||
|     """ | ||||
|  | ||||
|     get_user_card = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.id == card_id) \ | ||||
|         .first() | ||||
|     get_user_card = (db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.id == card_id)  | ||||
|         .first()) | ||||
|  | ||||
|     card_schema = Card_Card_schema(many=False) | ||||
|     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"]) | ||||
| 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 | ||||
|     """ | ||||
|  | ||||
|     get_new_main_card = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.user_id == user_id) \ | ||||
|         .filter(Card_Card.id == card_id) \ | ||||
|         .first() | ||||
|     get_new_main_card = (db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.user_id == user_id)  | ||||
|         .filter(Card_Card.id == card_id)  | ||||
|         .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: | ||||
|         get_other_card.main_card = False | ||||
|         db.session.add(get_other_card) | ||||
| @@ -148,45 +127,6 @@ def set_main_card(user_id, card_id): | ||||
|     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"]) | ||||
| @@ -195,12 +135,414 @@ def remove_user_card(card_id): | ||||
|     removes a card  | ||||
|     """ | ||||
|  | ||||
|     get_card = db.session \ | ||||
|         .query(Card_Card) \ | ||||
|         .filter(Card_Card.id == card_id) \ | ||||
|         .first() | ||||
|     get_card = (db.session  | ||||
|         .query(Card_Card)  | ||||
|         .filter(Card_Card.id == card_id)  | ||||
|         .first()) | ||||
|  | ||||
|     db.session.delete(get_card) | ||||
|     db.session.commit() | ||||
|  | ||||
|     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
									
								
							
							
						
						
									
										7
									
								
								app/promo/__init__.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										182
									
								
								app/promo/views.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										0
									
								
								app/query/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										50
									
								
								app/query/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										50
									
								
								app/query/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,20 +1,28 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import current_user | ||||
| from datetime import date, timedelta | ||||
| from flask import  jsonify | ||||
| from app.query import query | ||||
| from app import db | ||||
| from datetime import datetime | ||||
| from app.classes.query import Query_StateList, \ | ||||
|     Query_DeliveryStatusList, \ | ||||
|     Query_DeliveryStatusList_Schema, \ | ||||
|     Query_StateList_Schema, \ | ||||
|     Query_CustomerTypeList, \ | ||||
|     Query_CustomerTypeList_Schema,\ | ||||
|     Query_EmployeeTypeList, \ | ||||
|     Query_EmployeeTypeList_Schema,\ | ||||
|     Query_ServiceTypeList,\ | ||||
|     Query_ServiceTypeList_Schema | ||||
| from app.classes.admin import Admin_Company | ||||
| from app.classes.query import (Query_StateList, | ||||
|     Query_DeliveryStatusList,  | ||||
|     Query_DeliveryStatusList_Schema,  | ||||
|     Query_StateList_Schema,  | ||||
|     Query_CustomerTypeList,  | ||||
|     Query_CustomerTypeList_Schema, | ||||
|     Query_EmployeeTypeList,  | ||||
|     Query_EmployeeTypeList_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"]) | ||||
| def get_state_list(): | ||||
| @@ -43,17 +51,6 @@ def get_customer_type_list(): | ||||
|     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"]) | ||||
| @@ -80,3 +77,6 @@ def get_delivery_status_list(): | ||||
|         .all() | ||||
|     delivery_schema = Query_DeliveryStatusList_Schema(many=True) | ||||
|     return jsonify(delivery_schema.dump(query_data)) | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										0
									
								
								app/reports/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								app/reports/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										36
									
								
								app/reports/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										36
									
								
								app/reports/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,16 +1,10 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import current_user | ||||
| from flask import jsonify | ||||
| from sqlalchemy.sql import func | ||||
| from datetime import date, timedelta | ||||
| from app.reports import reports | ||||
| 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.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 | ||||
|  | ||||
|  | ||||
| @@ -21,4 +15,28 @@ def oil_total_gallons(): | ||||
|     .group_by(Delivery_Delivery.id)\ | ||||
|     .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
									
								
							
							
						
						
									
										0
									
								
								app/search/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										29
									
								
								app/search/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										29
									
								
								app/search/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,16 +1,9 @@ | ||||
| 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 import db | ||||
| from datetime import datetime | ||||
|  | ||||
| from app.classes.auth import Auth_User | ||||
| from sqlalchemy import or_ | ||||
| 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 | ||||
|  | ||||
|  | ||||
| @@ -26,14 +19,15 @@ def search_customers(): | ||||
|     search = search.replace("!", "") | ||||
|     search = search.replace("#", "") | ||||
|     search = search.replace("@", "") | ||||
|     search = search.replace("$", "") | ||||
|     # search by last name | ||||
|     if search_type == '@': | ||||
|         search = search[1:] | ||||
|         customer_list = (db.session | ||||
|                          .query(Customer_Customer) | ||||
|                          .filter(Customer_Customer.customer_first_name.ilike(search)) | ||||
|                          .filter(Customer_Customer.customer_last_name.ilike(search)) | ||||
|                          .all()) | ||||
|  | ||||
|     # Customer Address | ||||
|     elif search_type == '!': | ||||
|  | ||||
|         search = search[::1] | ||||
| @@ -48,11 +42,22 @@ def search_customers(): | ||||
|                          .query(Customer_Customer) | ||||
|                          .filter(Customer_Customer.customer_phone_number.ilike(search)) | ||||
|                          .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: | ||||
|         customer_list = (db.session | ||||
|                          .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()) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,397 +1,415 @@ | ||||
| from flask import request, jsonify | ||||
| from flask_login import current_user, login_required | ||||
| from app.service import service | ||||
| 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.service import (Service_Call, | ||||
|                                  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.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)) | ||||
|  | ||||
| from datetime import datetime, date, timedelta | ||||
| from app.classes.customer import (Customer_Customer) | ||||
| from app.classes.service import (Service_Service, | ||||
|                                   Service_Service_schema, Service_Parts, Service_Parts_schema, | ||||
|                                   Service_Plans, Service_Plans_schema | ||||
|                                   ) | ||||
| from app.classes.auto import Auto_Delivery | ||||
|  | ||||
| @service.route("/all", methods=["GET"]) | ||||
| def service_view_all(): | ||||
|     """ | ||||
|     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"] | ||||
| def get_all_service_calls(): | ||||
|     try: | ||||
|         if request.json["credit_card_id"]: | ||||
|             card_payment_id = request.json["credit_card_id"] | ||||
|         all_services = Service_Service.query.all() | ||||
|         color_map = { | ||||
|             0: {"backgroundColor": "blue", "textColor": "white"}, 1: {"backgroundColor": "red", "textColor": "white"}, | ||||
|             2: {"backgroundColor": "green", "textColor": "white"}, 3: {"backgroundColor": "yellow", "textColor": "black"}, | ||||
|             4: {"backgroundColor": "black", "textColor": "white"} | ||||
|         } | ||||
|         service_type_map = {0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other'} | ||||
|          | ||||
|         calendar_events = [] | ||||
|         for service_record in all_services: | ||||
|             service_type_text = service_type_map.get(service_record.type_service_call, 'Service') | ||||
|             event_title = f"{service_type_text}: {service_record.customer_name}" | ||||
|             event_colors = color_map.get(service_record.type_service_call, {"backgroundColor": "gray", "textColor": "white"}) | ||||
|              | ||||
|             # Use the schema to safely get the date string | ||||
|             serialized_record = Service_Service_schema().dump(service_record) | ||||
|             start_date = serialized_record.get('scheduled_date') | ||||
|  | ||||
|             event_data = { | ||||
|                 "id": service_record.id,  | ||||
|                 "title": event_title,  | ||||
|                 "start": start_date, | ||||
|                 "end": None, | ||||
|                 "extendedProps": { | ||||
|                     "customer_id": service_record.customer_id, | ||||
|                     "description": service_record.description, | ||||
|                     "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) | ||||
|              | ||||
|         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 | ||||
|  | ||||
|  | ||||
| # --- THIS IS THE FIX --- | ||||
| # The logic from /all has been copied here to ensure a consistent data structure. | ||||
| @service.route("/upcoming", methods=["GET"]) | ||||
| def get_upcoming_service_calls(): | ||||
|     """ | ||||
|     Fetches a list of all future service calls from today onwards. | ||||
|     """ | ||||
|     now = datetime.now() | ||||
|     upcoming_services = ( | ||||
|         Service_Service.query | ||||
|         .filter(Service_Service.scheduled_date >= now) | ||||
|         .order_by(Service_Service.scheduled_date.asc()) | ||||
|         .limit(100) | ||||
|         .all() | ||||
|     ) | ||||
|      | ||||
|     service_schema = Service_Service_schema(many=True) | ||||
|     result = service_schema.dump(upcoming_services) | ||||
|      | ||||
|   | ||||
|     return jsonify(result), 200 | ||||
|  | ||||
|  | ||||
| @service.route("/past", methods=["GET"]) | ||||
| def get_past_service_calls(): | ||||
|     """ | ||||
|     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() | ||||
|     ) | ||||
|  | ||||
|     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() | ||||
|  | ||||
|         # Return success response | ||||
|         return jsonify({ | ||||
|             "ok": True, | ||||
|             "service_id": id, | ||||
|             "service_cost_updated": new_cost_float, | ||||
|             "message": f"Service {id} cost updated to ${new_cost_float}" | ||||
|         }), 200 | ||||
|  | ||||
|     except Exception as e: | ||||
|         db.session.rollback() | ||||
|         print(f"Error updating service cost for service {id}: {str(e)}") | ||||
|         return jsonify({"error": str(e)}), 500 | ||||
|  | ||||
| @service.route("/update/<int:id>", methods=["PUT"]) | ||||
| def update_service_call(id): | ||||
|     service_record = Service_Service.query.get_or_404(id) | ||||
|     data = request.get_json() | ||||
|     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() | ||||
|         service_schema = Service_Service_schema(many=False) | ||||
|         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("/plans/active", methods=["GET"]) | ||||
| def get_active_service_plans(): | ||||
|     """ | ||||
|     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) | ||||
|  | ||||
|         # Add customer info to each plan | ||||
|         for plan in result: | ||||
|             customer = Customer_Customer.query.get(plan['customer_id']) | ||||
|             if customer: | ||||
|                 plan['customer_name'] = f"{customer.customer_first_name} {customer.customer_last_name}" | ||||
|                 plan['customer_address'] = customer.customer_address | ||||
|                 plan['customer_town'] = customer.customer_town | ||||
|  | ||||
|         return jsonify(result), 200 | ||||
|     except Exception as e: | ||||
|         return jsonify({"error": str(e)}), 500 | ||||
|  | ||||
|  | ||||
| @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: | ||||
|             card_payment_id = None | ||||
|     except: | ||||
|         card_payment_id = None | ||||
|             return jsonify(None), 200 | ||||
|     except Exception as e: | ||||
|         return jsonify({"error": str(e)}), 500 | ||||
|  | ||||
|     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 | ||||
| @service.route("/plans/create", methods=["POST"]) | ||||
| def create_service_plan(): | ||||
|     """ | ||||
|     Create a new service plan for a customer | ||||
|     """ | ||||
|     data = request.get_json() | ||||
|     if not data: | ||||
|         return jsonify({"error": "No data provided"}), 400 | ||||
|  | ||||
|     try: | ||||
|         new_plan = Service_Plans( | ||||
|             customer_id=data['customer_id'], | ||||
|             contract_plan=data['contract_plan'], | ||||
|             contract_years=data['contract_years'], | ||||
|             contract_start_date=datetime.fromisoformat(data['contract_start_date']) | ||||
|         ) | ||||
|         db.session.add(new_plan) | ||||
|         db.session.commit() | ||||
|  | ||||
|         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("/plans/update/<int:customer_id>", methods=["PUT"]) | ||||
| def update_service_plan(customer_id): | ||||
|     """ | ||||
|     Update existing service plan for a customer | ||||
|     """ | ||||
|     data = request.get_json() | ||||
|     if not data: | ||||
|         return jsonify({"error": "No data provided"}), 400 | ||||
|  | ||||
|     try: | ||||
|         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) | ||||
|  | ||||
|         plan.contract_plan = data.get('contract_plan', plan.contract_plan) | ||||
|         plan.contract_years = data.get('contract_years', plan.contract_years) | ||||
|         if data.get('contract_start_date'): | ||||
|             plan.contract_start_date = datetime.fromisoformat(data['contract_start_date']) | ||||
|  | ||||
|         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: | ||||
|         delivery_payment_method = 3 | ||||
|         return jsonify({ | ||||
|             "customer_id": customer_id, "oil_filter": "", "oil_filter_2": "", | ||||
|             "oil_nozzle": "", "oil_nozzle_2": "", "hot_water_tank": 0 | ||||
|         }), 200 | ||||
|  | ||||
|     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 | ||||
| @service.route("/parts/update/<int:customer_id>", methods=["POST"]) | ||||
| def update_service_parts(customer_id): | ||||
|     try: | ||||
|         data = request.get_json() | ||||
|  | ||||
|     date_object = datetime.strptime(scheduled_date_date, '%Y-%m-%d').date() | ||||
|         if not data: | ||||
|             return jsonify({"error": "No data provided"}), 400 | ||||
|  | ||||
|     get_tech = (db.session | ||||
|                 .query(Employee_Employee) | ||||
|                 .first()) | ||||
|         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) | ||||
|  | ||||
|     create_a_call = Service_Call( | ||||
|         customer_id=get_customer.id, | ||||
|         customer_last_name=get_customer.customer_last_name, | ||||
|         customer_first_name=get_customer.customer_first_name, | ||||
|         customer_town=get_customer.customer_town, | ||||
|         customer_state=get_customer.customer_state, | ||||
|         customer_zip=get_customer.customer_zip, | ||||
|         customer_apt=get_customer.customer_apt, | ||||
|         customer_address=get_customer.customer_address, | ||||
|         status=0, | ||||
|         service_type=get_service_type, | ||||
|         when_called=now, | ||||
|         scheduled_date=date_object, | ||||
|         when_serviced=None, | ||||
|         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, | ||||
|     ) | ||||
|         # 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.add(create_a_call) | ||||
|     db.session.flush() | ||||
|  | ||||
|     create_new_note = Service_Call_Notes_Dispatcher( | ||||
|         service_call_id=create_a_call.id, | ||||
|         dispatcher_subject=service_subject, | ||||
|         dispatcher_notes=service_note, | ||||
|         time_added=now, | ||||
|         dispatcher_id=dispatcher_id, | ||||
|         dispatcher_name=None, | ||||
|     ) | ||||
|  | ||||
|     db.session.add(create_new_note) | ||||
|     db.session.commit() | ||||
|  | ||||
|     db.session.commit() | ||||
|  | ||||
|     return jsonify({"ok": True, | ||||
|                     'user_id': get_customer.id, | ||||
|                     'service_id': create_a_call.id, | ||||
|                     }), 200 | ||||
|  | ||||
| @service.route("/delete/<int:service_id>", methods=["DELETE"]) | ||||
| def service_delete_call(service_id): | ||||
|     """ | ||||
|     delete a service call | ||||
|     """ | ||||
|     get_call_to_delete = (db.session | ||||
|                           .query(Service_Call) | ||||
|                           .filter(Service_Call.id == service_id) | ||||
|                           .first()) | ||||
|     db.session.delete(get_call_to_delete) | ||||
|     db.session.commit() | ||||
|  | ||||
|     return jsonify({"ok": True}), 200 | ||||
|         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("/edit/<int:service_id>", methods=["PUT"]) | ||||
| def service_edit_call(service_id): | ||||
|     """ | ||||
|     Update a service call | ||||
|     """ | ||||
| @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 | ||||
|  | ||||
|     get_service_type = request.json["type_of_service"] | ||||
|     scheduled_date_date = request.json["date_scheduled"] | ||||
|     dispatcher_subject_taken = request.json["dispatcher_subject_taken"] | ||||
|     dispatcher_notes_taken = request.json["dispatcher_notes_taken"] | ||||
|     tech_id = request.json["tech_id"] | ||||
|     # 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 | ||||
|  | ||||
|     card_payment = request.json["credit"] | ||||
|     cash_payment = request.json["cash"] | ||||
|  | ||||
|     if request.json["credit_card_id"]: | ||||
|         card_payment_id = request.json["credit_card_id"] | ||||
|     else: | ||||
|         card_payment_id = None | ||||
|  | ||||
|     get_tech = db.session \ | ||||
|         .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"]) | ||||
| def service_update_service_type(service_id): | ||||
|     """ | ||||
|     Update a service call diagnosis | ||||
|     """ | ||||
|     get_service_type = request.json["service_type"] | ||||
|  | ||||
|     get_service_call = db.session \ | ||||
|         .query(Service_Call) \ | ||||
|         .filter(Service_Call.service_id == service_id) \ | ||||
|         .first() | ||||
|  | ||||
|     get_service_call.service_type = get_service_type | ||||
|  | ||||
|     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(create_new_note) | ||||
|     db.session.commit() | ||||
|  | ||||
|     return jsonify({"ok": True}), 200 | ||||
|  | ||||
|  | ||||
| @service.route("/note/technician/<int:service_id>", methods=["PUT"]) | ||||
| def service_create_note_technician(service_id): | ||||
|     """ | ||||
|     Technician can create notes on the call | ||||
|     """ | ||||
|  | ||||
|     service_technician_notes = request.json["technician_comments"] | ||||
|     now = datetime.utcnow() | ||||
|  | ||||
|     user = db.session \ | ||||
|         .query(Auth_User) \ | ||||
|         .filter(Auth_User.id == current_user.id) \ | ||||
|         .first() | ||||
|  | ||||
|     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() | ||||
|  | ||||
|     return jsonify({"ok": True}), 200 | ||||
|     try: | ||||
|         db.session.commit() | ||||
|         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
									
								
							
							
						
						
									
										7
									
								
								app/social/__init__.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										77
									
								
								app/social/views.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										0
									
								
								app/stats/__init__.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										213
									
								
								app/stats/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										213
									
								
								app/stats/views.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,31 +1,218 @@ | ||||
| from flask import jsonify | ||||
| from sqlalchemy import func | ||||
| from datetime import date | ||||
| from app.stats import stats | ||||
| import datetime | ||||
| from app import db | ||||
| 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"]) | ||||
| def get_delivery_today(): | ||||
|     today_deliveries = (db.session | ||||
| def total_deliveries_today(): | ||||
|     total_stops = (db.session | ||||
|                         .query(Delivery_Delivery) | ||||
|                         .order_by(func.date(Delivery_Delivery.expected_delivery_date) == date.today()) | ||||
|                         .filter(Delivery_Delivery.expected_delivery_date == date.today()) | ||||
|                         .count()) | ||||
|  | ||||
|     return jsonify({"ok": True, | ||||
|                     'data': today_deliveries, | ||||
|                     'data': total_stops, | ||||
|                     }), 200 | ||||
|  | ||||
|  | ||||
| @stats.route("/service/count/today", methods=["GET"]) | ||||
| def get_service_today(): | ||||
|     today_calls = (db.session | ||||
|                         .query(Service_Call) | ||||
|                         .order_by(func.date(Service_Call.scheduled_date) == date.today()) | ||||
| @stats.route("/delivery/count/delivered/today", methods=["GET"]) | ||||
| def total_deliveries_today_finished(): | ||||
|     total_stops = (db.session | ||||
|                         .query(Delivery_Delivery) | ||||
|                         .filter(Delivery_Delivery.expected_delivery_date == date.today()) | ||||
|                         .filter((Delivery_Delivery.delivery_status == 10)) | ||||
|                         .count()) | ||||
|      | ||||
|     return jsonify({"ok": True, | ||||
|                     'data': today_calls, | ||||
|                     'data': total_stops, | ||||
|                     }), 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
									
								
							
							
						
						
									
										7
									
								
								app/ticket/__init__.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										14
									
								
								app/ticket/views.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										24
									
								
								config.py
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										1
									
								
								info_authorize_transactiopn_type.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								info_authorize_transactiopn_type.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # 0 = charge, 1 = auth, 3 = capture | ||||
							
								
								
									
										8
									
								
								info_delivery_status.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								info_delivery_status.txt
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										7
									
								
								info_home_type.txt
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										13
									
								
								info_paymant_type.txt
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										7
									
								
								info_state_list.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| 0: MA | ||||
| 1: RI | ||||
| 2: NH | ||||
| 3: ME | ||||
| 4: VT | ||||
| 5: CT | ||||
| 6: NY | ||||
							
								
								
									
										17
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								nginx.conf
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
| @@ -1,19 +1,35 @@ | ||||
| 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 | ||||
| bcrypt==4.3.0 | ||||
| blinker==1.9.0 | ||||
| cachelib==0.13.0 | ||||
| click==8.1.8 | ||||
| Flask==3.1.0 | ||||
| Flask-Bcrypt==1.0.1 | ||||
| flask-cors==5.0.1 | ||||
| Flask-Login==0.6.3 | ||||
| Flask-Mail==0.10.0 | ||||
| flask-marshmallow==1.3.0 | ||||
| Flask-Moment==1.0.6 | ||||
| Flask-Paranoid==0.3.0 | ||||
| Flask-Session==0.8.0 | ||||
| Flask-SQLAlchemy==3.1.1 | ||||
| Flask-WTF==1.2.2 | ||||
| geographiclib==2.0 | ||||
| geopy==2.4.1 | ||||
| greenlet==3.2.2 | ||||
| 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
									
								
							
							
						
						
									
										20
									
								
								requirements.txt.bak
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										11
									
								
								runProduction.py
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										55
									
								
								settings_dev.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										58
									
								
								settings_local.py
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										56
									
								
								settings_prod.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										8
									
								
								start.sh
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										13
									
								
								update_requirements.sh
									
									
									
									
									
										Executable 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." | ||||
		Reference in New Issue
	
	Block a user