init commit
This commit is contained in:
3
transportmanager/server/routes/__init__.py
Normal file
3
transportmanager/server/routes/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from flask import Blueprint
|
||||
|
||||
# Placeholder for blueprint registration if needed dynamically
|
||||
204
transportmanager/server/routes/auth.py
Normal file
204
transportmanager/server/routes/auth.py
Normal file
@@ -0,0 +1,204 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from utils.email import send_email, send_gmail
|
||||
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
|
||||
from flask_jwt_extended import decode_token
|
||||
import datetime
|
||||
import random
|
||||
import os
|
||||
from datetime import timezone
|
||||
|
||||
from models.user import Users
|
||||
|
||||
auth_bp = Blueprint("auth", __name__)
|
||||
|
||||
@auth_bp.route("/register", methods=["POST"])
|
||||
def register():
|
||||
users = Users()
|
||||
data = request.get_json()
|
||||
name = data.get("name")
|
||||
email = data.get("email")
|
||||
password = data.get("password")
|
||||
|
||||
if not name or not email or not password:
|
||||
return jsonify({"error": "Missing required fields"}), 400
|
||||
|
||||
existing_user = users.get_user_by_email(email)
|
||||
if existing_user:
|
||||
return jsonify({"error": "User already exists"}), 409
|
||||
|
||||
password_hash = generate_password_hash(password)
|
||||
users.insert_user(name, email, password_hash)
|
||||
|
||||
return jsonify({"message": "User registered successfully!"}), 201
|
||||
|
||||
|
||||
@auth_bp.route("/login", methods=["POST"])
|
||||
def login():
|
||||
users = Users()
|
||||
data = request.get_json()
|
||||
email = data.get("email", "").strip().lower()
|
||||
password = data.get("password", "")
|
||||
|
||||
if not email or not password:
|
||||
return jsonify({"error": "Missing email or password"}), 400
|
||||
|
||||
user = users.get_user_by_email(email)
|
||||
if not user or not check_password_hash(user["password_hash"], password):
|
||||
return jsonify({"error": "Invalid credentials"}), 401
|
||||
|
||||
otp_code = str(random.randint(100000, 999999))
|
||||
expiration = datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)
|
||||
users.update_user_otp(user["id"], otp_code, expiration)
|
||||
|
||||
send_gmail(
|
||||
to_email=user["email"],
|
||||
subject="Your Login Verification Code",
|
||||
body=f"Your login verification code is: {otp_code}"
|
||||
)
|
||||
|
||||
return jsonify({"message": "Verification code sent to your email."}), 200
|
||||
|
||||
|
||||
@auth_bp.route("/verify_code", methods=["POST"])
|
||||
def verify_code():
|
||||
users = Users()
|
||||
data = request.get_json()
|
||||
email = data.get("email", "").strip().lower()
|
||||
code = data.get("code", "")
|
||||
|
||||
if not email or not code:
|
||||
return jsonify({"error": "Missing email or verification code"}), 400
|
||||
|
||||
user = users.get_user_by_email(email)
|
||||
#-----------------------------------------------> for testing only remove in prod
|
||||
#if email != 'test@test.com':
|
||||
#-----------------------------------------------> for testing only remove in prod
|
||||
if not user or user.get("otp_code") != code:
|
||||
return jsonify({"error": "Invalid code"}), 401
|
||||
|
||||
exp = user.get("otp_expiration")
|
||||
# Normalize to aware UTC datetime for safe comparison across SQLite (string) and Postgres (datetime)
|
||||
now_utc = datetime.datetime.now(timezone.utc)
|
||||
if isinstance(exp, str):
|
||||
try:
|
||||
exp_dt = datetime.datetime.fromisoformat(exp)
|
||||
except Exception:
|
||||
return jsonify({"error": "Invalid expiration format"}), 500
|
||||
if exp_dt.tzinfo is None:
|
||||
exp_dt = exp_dt.replace(tzinfo=timezone.utc)
|
||||
else:
|
||||
# Assume a datetime object from DB driver
|
||||
exp_dt = exp
|
||||
if exp_dt is None:
|
||||
return jsonify({"error": "Missing expiration"}), 500
|
||||
if exp_dt.tzinfo is None:
|
||||
exp_dt = exp_dt.replace(tzinfo=timezone.utc)
|
||||
if now_utc > exp_dt:
|
||||
return jsonify({"error": "Verification code has expired"}), 403
|
||||
|
||||
users.clear_user_otp(user["id"])
|
||||
|
||||
access_token = create_access_token(
|
||||
identity=str(user["id"]),
|
||||
expires_delta=datetime.timedelta(hours=12)
|
||||
)
|
||||
return jsonify({
|
||||
"message": "Login successful",
|
||||
"access_token": access_token
|
||||
}), 200
|
||||
|
||||
|
||||
@auth_bp.route("/forgot_password", methods=["POST"])
|
||||
def forgot_password():
|
||||
users = Users()
|
||||
data = request.get_json()
|
||||
email = data.get("email", "").strip().lower()
|
||||
|
||||
if not email:
|
||||
return jsonify({"error": "Email is required"}), 400
|
||||
|
||||
user = users.get_user_by_email(email)
|
||||
if user:
|
||||
reset_token = create_access_token(
|
||||
identity=user["id"],
|
||||
expires_delta=datetime.timedelta(minutes=15),
|
||||
additional_claims={"purpose": "password_reset"}
|
||||
)
|
||||
|
||||
send_gmail(
|
||||
to_email=user["email"],
|
||||
subject="Password Reset Request",
|
||||
body=(
|
||||
"Click the link to reset your password: "
|
||||
f"{os.getenv('FRONTEND_BASE_URL', 'http://127.0.0.1:5100')}/reset_password?token={reset_token}"
|
||||
)
|
||||
)
|
||||
|
||||
return jsonify({"message": "If this email is registered, a reset link has been sent."}), 200
|
||||
|
||||
|
||||
@auth_bp.route("/reset_password", methods=["POST"])
|
||||
def reset_password():
|
||||
users = Users()
|
||||
data = request.get_json()
|
||||
token = data.get("token", "")
|
||||
new_password = data.get("new_password", "")
|
||||
|
||||
if not token or not new_password:
|
||||
return jsonify({"error": "Missing token or new password"}), 400
|
||||
|
||||
try:
|
||||
decoded_token = decode_token(token)
|
||||
if decoded_token.get("purpose") != "password_reset":
|
||||
return jsonify({"error": "Invalid token purpose"}), 403
|
||||
except Exception:
|
||||
return jsonify({"error": "Invalid or expired token"}), 403
|
||||
|
||||
user_id = decoded_token["sub"]
|
||||
user = users.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
|
||||
password_hash = generate_password_hash(new_password)
|
||||
users.update_user_password(user_id, password_hash)
|
||||
|
||||
return jsonify({"message": "Password has been reset successfully."}), 200
|
||||
|
||||
|
||||
@auth_bp.route("/me", methods=["GET"])
|
||||
@jwt_required()
|
||||
def me():
|
||||
users = Users()
|
||||
user_id = get_jwt_identity()
|
||||
user = users.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
|
||||
return jsonify({
|
||||
"id": user["id"],
|
||||
"name": user["name"],
|
||||
"contact_name": user['contact_name'],
|
||||
"email": user["email"],
|
||||
"phone": user["phone"],
|
||||
"register_number": user["register_number"],
|
||||
"vat":user["vat"],
|
||||
"address": user["address"],
|
||||
"logo_filename": user["logo_filename"],
|
||||
"terms": user["terms"],
|
||||
"first_order_number": user["first_order_number"],
|
||||
"created_at": user["created_at"],
|
||||
"user_role": user["user_role"]
|
||||
}), 200
|
||||
|
||||
|
||||
# Validate token endpoint
|
||||
@auth_bp.route("/validate_token", methods=["GET"])
|
||||
@jwt_required()
|
||||
def validate_token():
|
||||
users = Users()
|
||||
user_id = get_jwt_identity()
|
||||
user = users.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
return jsonify({"message": "Token is valid"}), 200
|
||||
65
transportmanager/server/routes/clients.py
Normal file
65
transportmanager/server/routes/clients.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from models.client import Clients
|
||||
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
|
||||
clients_bp = Blueprint("clients", __name__, url_prefix="/clients")
|
||||
|
||||
@clients_bp.route("/", methods=["GET"])
|
||||
@jwt_required()
|
||||
def list_clients():
|
||||
clients_db = Clients()
|
||||
user_id = get_jwt_identity()
|
||||
clients = clients_db.get_all_by_user(user_id)
|
||||
return jsonify(clients), 200
|
||||
|
||||
@clients_bp.route("/", methods=["POST"])
|
||||
@jwt_required()
|
||||
def create_client():
|
||||
clients_db = Clients()
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
client_id = clients_db.create(
|
||||
user_id=user_id,
|
||||
name=data["name"],
|
||||
address=data["address"],
|
||||
register_number=data["register_number"],
|
||||
contact_person=data["contact_person"],
|
||||
phone=data["phone"],
|
||||
email=data["email"],
|
||||
vat = data["vat"]
|
||||
)
|
||||
return jsonify({"message": "Client created", "id": client_id}), 201
|
||||
|
||||
@clients_bp.route("/<int:client_id>", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_client(client_id):
|
||||
clients_db = Clients()
|
||||
data = request.get_json()
|
||||
name=data["name"]
|
||||
address=data["address"]
|
||||
register_number=data["register_number"]
|
||||
contact_person=data["contact_person"]
|
||||
phone=data["phone"]
|
||||
email=data["email"]
|
||||
vat = data["vat"]
|
||||
clients_db.update(client_id, name, address, register_number, contact_person, phone, email, vat)
|
||||
return jsonify({"message": "Client updated"}), 200
|
||||
|
||||
@clients_bp.route("/<int:client_id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def delete_client(client_id):
|
||||
clients_db = Clients()
|
||||
success = clients_db.delete(client_id)
|
||||
if not success:
|
||||
return jsonify({"message": "Client not found or unauthorized"}), 404
|
||||
return jsonify({"message": "Client deleted"}), 200
|
||||
|
||||
@clients_bp.route("/<int:client_id>", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_client(client_id):
|
||||
clients_db = Clients()
|
||||
client = clients_db.get_by_id(client_id)
|
||||
if not client:
|
||||
return jsonify({"message": "Client not found"}), 404
|
||||
return jsonify(client), 200
|
||||
73
transportmanager/server/routes/destinations.py
Normal file
73
transportmanager/server/routes/destinations.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from models.destinations import Destinations
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from utils.maps import AdressCoordinates
|
||||
|
||||
destinations_bp = Blueprint("destinations", __name__, url_prefix="/destinations")
|
||||
|
||||
@destinations_bp.route("/", methods=["GET"])
|
||||
@jwt_required()
|
||||
def list_destinations():
|
||||
destinations_db = Destinations()
|
||||
user_id = get_jwt_identity()
|
||||
destinations = destinations_db.get_all_by_user(user_id)
|
||||
return jsonify([dict(d) for d in destinations]), 200
|
||||
|
||||
@destinations_bp.route("/", methods=["POST"])
|
||||
@jwt_required()
|
||||
def create_destination():
|
||||
destinations_db = Destinations()
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
destination_id = destinations_db.create(user_id, data.get("name"), data.get("address"))
|
||||
# coordinates = AdressCoordinates(data.get("address"))
|
||||
# lat_log = coordinates.open_Maps_by_address()
|
||||
# if lat_log:
|
||||
# latitude = lat_log['latitude']
|
||||
# longitude = lat_log['longitude']
|
||||
# destinations_db.add_gps_coordinates(destination_id, latitude, longitude)
|
||||
return jsonify({"id": destination_id, "message": "Destination created"}), 201
|
||||
|
||||
@destinations_bp.route("/<int:id>", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_destination(id):
|
||||
destinations_db = Destinations()
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
destinations_db.update(id, user_id, data.get("name"), data.get("address"))
|
||||
coordinates = AdressCoordinates(data.get("address"))
|
||||
lat_log = coordinates.open_Maps_by_address()
|
||||
if lat_log:
|
||||
latitude = lat_log['latitude']
|
||||
longitude = lat_log['longitude']
|
||||
destinations_db.add_gps_coordinates(id, latitude, longitude)
|
||||
|
||||
return jsonify({"message": "Destination updated"}), 200
|
||||
|
||||
@destinations_bp.route("/<int:id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def delete_destination(id):
|
||||
destinations_db = Destinations()
|
||||
success = destinations_db.delete(id)
|
||||
if not success:
|
||||
return jsonify({"message": "Destination not found or unauthorized"}), 404
|
||||
return "", 204
|
||||
|
||||
|
||||
# New route to update GPS coordinates of a destination
|
||||
@destinations_bp.route("/<int:id>/coordinates", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_coordinates(id):
|
||||
destinations_db = Destinations()
|
||||
data = request.get_json()
|
||||
latitude = data.get("latitude")
|
||||
longitude = data.get("longitude")
|
||||
|
||||
if latitude is None or longitude is None:
|
||||
return jsonify({"message": "Latitude and longitude are required"}), 400
|
||||
|
||||
success = destinations_db.add_gps_coordinates(id, latitude, longitude)
|
||||
if not success:
|
||||
return jsonify({"message": "Failed to update coordinates"}), 404
|
||||
|
||||
return jsonify({"message": "Coordinates updated"}), 200
|
||||
259
transportmanager/server/routes/orders_out.py
Normal file
259
transportmanager/server/routes/orders_out.py
Normal file
@@ -0,0 +1,259 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models.order_out import OrdersOut
|
||||
from models.user import Users
|
||||
from models.transporters import Transporters
|
||||
from datetime import datetime
|
||||
from utils.pdf import generate_order_pdf
|
||||
from utils.cancel_order import cancel_order_pdf
|
||||
import os
|
||||
|
||||
from flask import send_from_directory
|
||||
from utils.email import send_gmail_with_attachment, send_custom_email_with_attachment
|
||||
|
||||
orders_bp = Blueprint("orders", __name__, url_prefix="/orders")
|
||||
|
||||
@orders_bp.route("/", methods=["POST"])
|
||||
@jwt_required()
|
||||
def create_order_route():
|
||||
user_id = get_jwt_identity()
|
||||
orders = OrdersOut()
|
||||
incoming_data = request.json
|
||||
#here we need to first implement the order pdf
|
||||
users = Users()
|
||||
user = users.get_user_by_id(user_id)
|
||||
logo_filename = user.get('logo_filename')
|
||||
logo_path = None
|
||||
if logo_filename:
|
||||
logo_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "..", "client", "assets", "images", logo_filename)
|
||||
)
|
||||
transporters = Transporters()
|
||||
transporter = transporters.get_transporter_by_id(incoming_data["transporter_id"])
|
||||
generate_order_pdf(order=incoming_data, user_data=user, transporter_data=transporter, logo_path=logo_path)
|
||||
#
|
||||
#try:
|
||||
order_data = {
|
||||
'user_id': user_id,
|
||||
'client_id': incoming_data["client_id"],
|
||||
'transporter_id': incoming_data["transporter_id"],
|
||||
'received_price': incoming_data["received_price"],
|
||||
'paid_price': incoming_data["paid_price"],
|
||||
'order_number': incoming_data["order_number"],
|
||||
'created_at': datetime.now(),
|
||||
'ldb_quantity': incoming_data["ldb_quantity"],
|
||||
'kg_quantity': incoming_data["kg_quantity"],
|
||||
'track_reg_number': incoming_data["track_reg_number"],
|
||||
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
||||
'products_description': incoming_data["products_description"],
|
||||
}
|
||||
order_id = orders.create_order(order_data)
|
||||
|
||||
for address in incoming_data["loading_addresses"]:
|
||||
data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['loading_address_id'],
|
||||
"informatins": address['loading_informatins'],
|
||||
"point_data": address['loading_date'],
|
||||
"point_hour": address['loading_hour'],
|
||||
"point_type": "loading"
|
||||
}
|
||||
orders.create_order_point(data)
|
||||
|
||||
for address in incoming_data["unloading_addresses"]:
|
||||
data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['unloading_address_id'],
|
||||
"informatins": address['unloading_informatins'],
|
||||
"point_data": address['unloading_date'],
|
||||
"point_hour": address['unloading_hour'],
|
||||
"point_type": "unloading"
|
||||
}
|
||||
orders.create_order_point(data)
|
||||
|
||||
|
||||
|
||||
return jsonify({"message": "Order created", "order_id": order_id}), 201
|
||||
#except Exception as e:
|
||||
# return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/<int:order_id>", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_order_route(order_id):
|
||||
orders = OrdersOut()
|
||||
data = request.json
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
if not order:
|
||||
return jsonify({"error": "Order not found"}), 404
|
||||
if str(order["user_id"]) != str(user_id):
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
try:
|
||||
orders.update_order({
|
||||
"id":data.get("id", order['id']),
|
||||
"client_id": data.get("client_id", order["client_id"]),
|
||||
"transporter_id": data.get("transporter_id", order["transporter_id"]),
|
||||
"received_price": data.get("received_price", order["received_price"]),
|
||||
"paid_price": data.get("paid_price", order["paid_price"]),
|
||||
"order_number": data.get("order_number", order["order_number"]),
|
||||
"ldb_quantity": data.get("ldb_quantity", order["ldb_quantity"]),
|
||||
"kg_quantity": data.get("kg_quantity", order["kg_quantity"]),
|
||||
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
||||
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
||||
"products_description": data.get("products_description", order["products_description"]),
|
||||
})
|
||||
|
||||
orders.delete_points_by_order_id(order_id)
|
||||
for address in data["loading_addresses"]:
|
||||
loading_data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['loading_address_id'],
|
||||
"informatins": address['loading_informatins'],
|
||||
"point_data": address['loading_date'],
|
||||
"point_hour": address['loading_hour'],
|
||||
"point_type": "loading"
|
||||
}
|
||||
orders.create_order_point(loading_data)
|
||||
|
||||
for address in data["unloading_addresses"]:
|
||||
unloading_data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['unloading_address_id'],
|
||||
"informatins": address['unloading_informatins'],
|
||||
"point_data": address['unloading_date'],
|
||||
"point_hour": address['unloading_hour'],
|
||||
"point_type": "unloading"
|
||||
}
|
||||
orders.create_order_point(unloading_data)
|
||||
|
||||
#regenerate pdf:
|
||||
incoming_data = data
|
||||
users = Users()
|
||||
user = users.get_user_by_id(user_id)
|
||||
transporters = Transporters()
|
||||
transporter = transporters.get_transporter_by_id(incoming_data["transporter_id"])
|
||||
logo_filename = user.get('logo_filename')
|
||||
logo_path = None
|
||||
if logo_filename:
|
||||
logo_path = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "..", "..", "client", "assets", "images", logo_filename)
|
||||
)
|
||||
generate_order_pdf(order=incoming_data, user_data=user, transporter_data=transporter, logo_path=logo_path)
|
||||
|
||||
return jsonify({"message": "Order updated", "order_id": order_id}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/<int:order_id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def delete_order_route(order_id):
|
||||
orders = OrdersOut()
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
if not order:
|
||||
return jsonify({"error": "Order not found"}), 404
|
||||
if order["user_id"] != user_id:
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
try:
|
||||
orders.delete_points_by_order_id(order_id)
|
||||
orders.delete_order(order_id)
|
||||
return jsonify({"message": "Order deleted"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/list", methods=["GET"])
|
||||
@jwt_required()
|
||||
def list_orders():
|
||||
orders = OrdersOut()
|
||||
user_id = get_jwt_identity()
|
||||
try:
|
||||
user_orders = orders.get_orders_by_user(user_id)
|
||||
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
||||
return jsonify(user_orders), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/<int:order_id>", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_order(order_id):
|
||||
orders = OrdersOut()
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
points = orders.get_order_points_by_order(order['id'])
|
||||
loading_points = []
|
||||
unloading_points = []
|
||||
for point in points:
|
||||
if point['point_type'] == 'loading':
|
||||
loading_points.append(point)
|
||||
else:
|
||||
unloading_points.append(point)
|
||||
order['loading_points'] = loading_points
|
||||
order['unloading_points'] = unloading_points
|
||||
if not order:
|
||||
return jsonify({"error": "Order not found"}), 404
|
||||
print(f'{order["user_id"]} {user_id}')
|
||||
print(f'{type(order["user_id"])} {type(user_id)}')
|
||||
if order["user_id"] != int(user_id):
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
return jsonify(order), 200
|
||||
|
||||
@orders_bp.route("/pdfs/<path:filename>", methods=["GET"])
|
||||
#@jwt_required()
|
||||
def serve_order_pdf(filename):
|
||||
pdf_folder = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "generated_pdfs"))
|
||||
print(pdf_folder)
|
||||
print(filename)
|
||||
return send_from_directory(pdf_folder, filename, mimetype="application/pdf")
|
||||
|
||||
@orders_bp.route("/send-email/gmail", methods=["POST"])
|
||||
@jwt_required()
|
||||
def send_email_with_gmail():
|
||||
data = request.json
|
||||
try:
|
||||
to_email = data["to_email"]
|
||||
subject = data["subject"]
|
||||
body = data["body"]
|
||||
filename = data["filename"]
|
||||
attachment_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "uploads", filename))
|
||||
|
||||
send_gmail_with_attachment(to_email, subject, body, attachment_path)
|
||||
return jsonify({"message": "Email sent successfully using Gmail"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/send-email/custom", methods=["POST"])
|
||||
@jwt_required()
|
||||
def send_email_with_custom_smtp():
|
||||
data = request.json
|
||||
try:
|
||||
to_email = data["to_email"]
|
||||
subject = data["subject"]
|
||||
body = data["body"]
|
||||
filename = data["filename"]
|
||||
smtp_host = data["smtp_host"]
|
||||
smtp_port = data["smtp_port"]
|
||||
smtp_user = data["smtp_user"]
|
||||
smtp_pass = data["smtp_pass"]
|
||||
|
||||
attachment_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "uploads", filename))
|
||||
|
||||
send_custom_email_with_attachment(to_email, subject, body, attachment_path, smtp_host, smtp_port, smtp_user, smtp_pass)
|
||||
return jsonify({"message": "Email sent successfully using custom SMTP"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_bp.route("/cancel/<int:order_id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def cancel_order(order_id):
|
||||
try:
|
||||
orders = OrdersOut()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
user_id = get_jwt_identity()
|
||||
pdf_name = f'order_{user_id}_{order['order_number']}.pdf'
|
||||
cancel_order_pdf(pdf_name)
|
||||
orders.cancel_order(order_id)
|
||||
return jsonify({"message": "The order was successfully canceled!"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
163
transportmanager/server/routes/ouders_in.py
Normal file
163
transportmanager/server/routes/ouders_in.py
Normal file
@@ -0,0 +1,163 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models.order_in import OrdersIn
|
||||
from models.transporters import Transporters
|
||||
from models.user import Users
|
||||
from datetime import datetime
|
||||
|
||||
orders_in_bp = Blueprint("orders_in", __name__, url_prefix="/orders_in")
|
||||
|
||||
@orders_in_bp.route("/", methods=["POST"])
|
||||
@jwt_required()
|
||||
def create_order_in_route():
|
||||
user_id = get_jwt_identity()
|
||||
orders = OrdersIn()
|
||||
incoming_data = request.json
|
||||
try:
|
||||
order_data = {
|
||||
'user_id': user_id,
|
||||
'client_id': incoming_data["client_id"],
|
||||
'received_price': incoming_data["received_price"],
|
||||
'order_number': incoming_data["order_number"],
|
||||
'created_at': datetime.now(),
|
||||
'ldb_quantity': incoming_data["ldb_quantity"],
|
||||
'kg_quantity': incoming_data["kg_quantity"],
|
||||
'track_reg_number': incoming_data["track_reg_number"],
|
||||
'trailer_reg_number': incoming_data["trailer_reg_number"],
|
||||
'products_description': incoming_data["products_description"],
|
||||
}
|
||||
order_id = orders.create_order(order_data)
|
||||
|
||||
for address in incoming_data["loading_addresses"]:
|
||||
data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['loading_address_id'],
|
||||
"informatins": address['loading_informatins'],
|
||||
"point_data": address['loading_date'],
|
||||
"point_hour": address['loading_hour'],
|
||||
"point_type": "loading"
|
||||
}
|
||||
orders.create_order_point(data)
|
||||
|
||||
for address in incoming_data["unloading_addresses"]:
|
||||
data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['unloading_address_id'],
|
||||
"informatins": address['unloading_informatins'],
|
||||
"point_data": address['unloading_date'],
|
||||
"point_hour": address['unloading_hour'],
|
||||
"point_type": "unloading"
|
||||
}
|
||||
orders.create_order_point(data)
|
||||
|
||||
return jsonify({"message": "Order in created", "order_id": order_id}), 201
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_in_bp.route("/<int:order_id>", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_order_route(order_id):
|
||||
orders = OrdersIn()
|
||||
data = request.json
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
if not order:
|
||||
return jsonify({"error": "Order in not found"}), 404
|
||||
if str(order["user_id"]) != str(user_id):
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
try:
|
||||
orders.update_order({
|
||||
"id":data.get("id", order['id']),
|
||||
"client_id": data.get("client_id", order["client_id"]),
|
||||
"received_price": data.get("received_price", order["received_price"]),
|
||||
"order_number": data.get("order_number", order["order_number"]),
|
||||
"ldb_quantity": data.get("ldb_quantity", order["ldb_quantity"]),
|
||||
"kg_quantity": data.get("kg_quantity", order["kg_quantity"]),
|
||||
"track_reg_number": data.get("track_reg_number", order["track_reg_number"]),
|
||||
"trailer_reg_number": data.get("trailer_reg_number", order["trailer_reg_number"]),
|
||||
"products_description": data.get("products_description", order["products_description"]),
|
||||
"user_id":user_id
|
||||
})
|
||||
|
||||
orders.delete_points_by_order_id(order_id)
|
||||
|
||||
for address in data["loading_addresses"]:
|
||||
loading_data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['loading_address_id'],
|
||||
"informatins": address['loading_informatins'],
|
||||
"point_data": address['loading_date'],
|
||||
"point_hour": address['loading_hour'],
|
||||
"point_type": "loading"
|
||||
}
|
||||
orders.create_order_point(loading_data)
|
||||
|
||||
for address in data["unloading_addresses"]:
|
||||
unloading_data = {
|
||||
"order_id": order_id,
|
||||
"destination_id": address['unloading_address_id'],
|
||||
"informatins": address['unloading_informatins'],
|
||||
"point_data": address['unloading_date'],
|
||||
"point_hour": address['unloading_hour'],
|
||||
"point_type": "unloading"
|
||||
}
|
||||
orders.create_order_point(unloading_data)
|
||||
|
||||
return jsonify({"message": "Order updated"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_in_bp.route("/<int:order_id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def delete_order_route(order_id):
|
||||
orders = OrdersIn()
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
if not order:
|
||||
return jsonify({"error": "Order in not found"}), 404
|
||||
if str(order["user_id"]) != str(user_id):
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
try:
|
||||
orders.delete_points_by_order_id(order_id)
|
||||
orders.delete_order(order_id)
|
||||
return jsonify({"message": "Order in deleted"}), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_in_bp.route("/list", methods=["GET"])
|
||||
@jwt_required()
|
||||
def list_orders():
|
||||
orders = OrdersIn()
|
||||
user_id = get_jwt_identity()
|
||||
try:
|
||||
user_orders = orders.get_orders_by_user(user_id)
|
||||
#result = [{"id": order["id"], "order_number": order["order_number"]} for order in user_orders]
|
||||
return jsonify(user_orders), 200
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 400
|
||||
|
||||
@orders_in_bp.route("/<int:order_id>", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_order(order_id):
|
||||
orders = OrdersIn()
|
||||
user_id = get_jwt_identity()
|
||||
order = orders.get_order_by_id(order_id)
|
||||
points = orders.get_order_points_by_order(order['id'])
|
||||
loading_points = []
|
||||
unloading_points = []
|
||||
for point in points:
|
||||
if point['point_type'] == 'loading':
|
||||
loading_points.append(point)
|
||||
else:
|
||||
unloading_points.append(point)
|
||||
order['loading_points'] = loading_points
|
||||
order['unloading_points'] = unloading_points
|
||||
if not order:
|
||||
return jsonify({"error": "Order not found"}), 404
|
||||
print(f'{order["user_id"]} {user_id}')
|
||||
print(f'{type(order["user_id"])} {type(user_id)}')
|
||||
if order["user_id"] != int(user_id):
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
return jsonify(order), 200
|
||||
144
transportmanager/server/routes/profile.py
Normal file
144
transportmanager/server/routes/profile.py
Normal file
@@ -0,0 +1,144 @@
|
||||
import os
|
||||
from werkzeug.utils import secure_filename
|
||||
from flask import current_app
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models.user import Users
|
||||
|
||||
profile_bp = Blueprint("profile", __name__)
|
||||
|
||||
|
||||
@profile_bp.route("/", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_profile():
|
||||
user_id = get_jwt_identity()
|
||||
users = Users()
|
||||
user = users.get_user_by_id(user_id) # Plain SQL method returning dict or None
|
||||
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
|
||||
return jsonify({
|
||||
"id": user["id"],
|
||||
"name": user["name"],
|
||||
"contact_name": user["contact_name"],
|
||||
"email": user["email"],
|
||||
"address": user["address"],
|
||||
"register_number": user["register_number"],
|
||||
"phone": user["phone"],
|
||||
"logo_filename": user["logo_filename"],
|
||||
"terms": user["terms"],
|
||||
"first_order_number": user["first_order_number"],
|
||||
"user_role": user["user_role"],
|
||||
"vat":user["vat"]
|
||||
})
|
||||
|
||||
|
||||
@profile_bp.route("/", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_profile():
|
||||
users = Users()
|
||||
user_id = get_jwt_identity()
|
||||
user = users.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
|
||||
data = request.get_json()
|
||||
update_data = {
|
||||
"name": data.get("name", user["name"]),
|
||||
"contact_name": data.get("contact_name", user["contact_name"]),
|
||||
"email": data.get("email", user["email"]),
|
||||
"address": data.get("address", user["address"]),
|
||||
"register_number": data.get("register_number", user["register_number"]),
|
||||
"phone": data.get("phone", user["phone"]),
|
||||
"logo_filename": data.get("logo_filename", user["logo_filename"]),
|
||||
"terms": data.get("terms", user["terms"]),
|
||||
"first_order_number": data.get("first_order_number", user["first_order_number"]),
|
||||
"user_id": user_id,
|
||||
"vat":data.get("vat", user["vat"]),
|
||||
}
|
||||
users.update_user(update_data)
|
||||
|
||||
return jsonify({"message": "Profile updated successfully"})
|
||||
|
||||
|
||||
@profile_bp.route("/logo", methods=["POST"])
|
||||
@jwt_required()
|
||||
def upload_logo():
|
||||
users = Users()
|
||||
if 'logo' not in request.files:
|
||||
return jsonify({"error": "Logo file is required"}), 400
|
||||
|
||||
file = request.files['logo']
|
||||
if file.filename == '':
|
||||
return jsonify({"error": "No selected file"}), 400
|
||||
|
||||
filename = secure_filename(file.filename)
|
||||
upload_dir = os.path.join(current_app.root_path, '..', 'instance', 'logos')
|
||||
os.makedirs(upload_dir, exist_ok=True)
|
||||
|
||||
filepath = os.path.join(upload_dir, filename)
|
||||
file.save(filepath)
|
||||
|
||||
user_id = get_jwt_identity()
|
||||
user = users.get_user_by_id(user_id)
|
||||
if not user:
|
||||
return jsonify({"error": "User not found"}), 404
|
||||
|
||||
# Update the logo filename in DB
|
||||
users.update_user_logo(user_id, {"logo_filename": filename})
|
||||
|
||||
return jsonify({"message": "Logo uploaded", "filename": filename}), 200
|
||||
|
||||
@profile_bp.route('/email')
|
||||
@jwt_required()
|
||||
def get_email_credentials():
|
||||
user_id = get_jwt_identity()
|
||||
users = Users()
|
||||
|
||||
credentials = users.get_email_credentials(user_id)
|
||||
if not credentials:
|
||||
return jsonify({"error": "Credentials not found"}), 404
|
||||
|
||||
return jsonify({
|
||||
'id': credentials['id'],
|
||||
'user_id': credentials['user_id'],
|
||||
'smtp_host': credentials['smtp_host'],
|
||||
'smtp_port': credentials['smtp_port'],
|
||||
'smtp_user': credentials['smtp_user'],
|
||||
'created_at': credentials['created_at']
|
||||
}), 200
|
||||
|
||||
@profile_bp.route('/email', methods=["POST"])
|
||||
@jwt_required()
|
||||
def insert_email_credentials():
|
||||
users = Users()
|
||||
user_id = get_jwt_identity()
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Credentials not found"}), 404
|
||||
smtp_host = data['smtp_host']
|
||||
smtp_port = data['smtp_port']
|
||||
smtp_user = data['smtp_user']
|
||||
|
||||
users.insert_email_credentials(user_id, smtp_host, smtp_port, smtp_user)
|
||||
|
||||
return jsonify({"message": "Credentials inserted successfully"}), 200
|
||||
|
||||
@profile_bp.route('/email', methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_email_credentials():
|
||||
users = Users()
|
||||
user_id = get_jwt_identity()
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({"error": "Credentials not found"}), 404
|
||||
smtp_host = data['smtp_host']
|
||||
smtp_port = data['smtp_port']
|
||||
smtp_user = data['smtp_user']
|
||||
|
||||
users.update_email_credentials(user_id, smtp_host, smtp_port, smtp_user)
|
||||
|
||||
return jsonify({"message": "Credentials updated successfully"}), 200
|
||||
42
transportmanager/server/routes/report.py
Normal file
42
transportmanager/server/routes/report.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models.order_out import OrdersOut # Your plain SQL model
|
||||
from datetime import datetime
|
||||
|
||||
report_bp = Blueprint("report", __name__, url_prefix="/report")
|
||||
|
||||
@report_bp.route("/profit", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_profit_report():
|
||||
try:
|
||||
user_id = get_jwt_identity()
|
||||
# Get filters from query params
|
||||
date_from = request.args.get("date_from")
|
||||
date_to = request.args.get("date_to")
|
||||
client_id = request.args.get("client_id")
|
||||
transporter_id = request.args.get("transporter_id")
|
||||
|
||||
# Use the plain SQL method that returns filtered orders list
|
||||
filters = {
|
||||
"user_id": user_id,
|
||||
"date_from": date_from,
|
||||
"date_to": date_to,
|
||||
"client_id": client_id,
|
||||
"transporter_id": transporter_id
|
||||
}
|
||||
|
||||
orders = OrdersOut.get_filtered_orders(filters) # Implement this method in your model
|
||||
|
||||
total_received = sum(float(o.get("price_received", 0) or 0) for o in orders)
|
||||
total_paid = sum(float(o.get("price_paid", 0) or 0) for o in orders)
|
||||
profit = total_received - total_paid
|
||||
|
||||
return jsonify({
|
||||
"total_received": total_received,
|
||||
"total_paid": total_paid,
|
||||
"profit": profit,
|
||||
"orders_count": len(orders)
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
65
transportmanager/server/routes/subscription.py
Normal file
65
transportmanager/server/routes/subscription.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from datetime import datetime, timedelta
|
||||
from models.subscription import Subscription
|
||||
from models.user import Users
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
subscription_bp = Blueprint("subscription", __name__, url_prefix="/subscription")
|
||||
|
||||
@subscription_bp.route("/", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_subscription():
|
||||
user_id = get_jwt_identity()
|
||||
subscription_model = Subscription()
|
||||
subscriptions = subscription_model.get_by_user_id(user_id)
|
||||
return jsonify(subscriptions), 200
|
||||
|
||||
@subscription_bp.route("/first_2_months", methods=["POST"])
|
||||
@jwt_required()
|
||||
def first_2_months_subscription():
|
||||
user_id = get_jwt_identity()
|
||||
users_model = Users()
|
||||
user = users_model.get_user_by_id(user_id)
|
||||
|
||||
subscription_model = Subscription()
|
||||
existing_sub = subscription_model.get_first_2_months_subscription_for_register_number(user["register_number"])
|
||||
|
||||
if existing_sub:
|
||||
return jsonify({"error": "First 2 months subscription already used for this company."}), 400
|
||||
|
||||
start_date = datetime.now()
|
||||
end_date = start_date + timedelta(days=60)
|
||||
subscription_model.create(user_id, "first_2_months", start_date.isoformat(), end_date.isoformat(), user["register_number"])
|
||||
|
||||
return jsonify({"message": "First 2 months subscription created."}), 201
|
||||
|
||||
@subscription_bp.route("/one_month", methods=["POST"])
|
||||
@jwt_required()
|
||||
def one_month_subscription():
|
||||
user_id = get_jwt_identity()
|
||||
start_date = datetime.now()
|
||||
end_date = start_date + timedelta(days=30)
|
||||
|
||||
users_model = Users()
|
||||
user = users_model.get_user_by_id(user_id)
|
||||
|
||||
subscription_model = Subscription()
|
||||
subscription_model.create(user_id, "monthly", start_date.isoformat(), end_date.isoformat(), user["register_number"])
|
||||
|
||||
return jsonify({"message": "1 month subscription created."}), 201
|
||||
|
||||
@subscription_bp.route("/one_year", methods=["POST"])
|
||||
@jwt_required()
|
||||
def one_year_subscription():
|
||||
user_id = get_jwt_identity()
|
||||
start_date = datetime.now()
|
||||
end_date = start_date + timedelta(days=365)
|
||||
|
||||
users_model = Users()
|
||||
user = users_model.get_user_by_id(user_id)
|
||||
|
||||
subscription_model = Subscription()
|
||||
subscription_model.create(user_id, "yearly", start_date.isoformat(), end_date.isoformat(), user["register_number"])
|
||||
|
||||
return jsonify({"message": "1 year subscription created."}), 201
|
||||
74
transportmanager/server/routes/transporters.py
Normal file
74
transportmanager/server/routes/transporters.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import jwt_required, get_jwt_identity
|
||||
from models.transporters import Transporters
|
||||
|
||||
transporters_bp = Blueprint("transporters", __name__, url_prefix="/transporters")
|
||||
|
||||
@transporters_bp.route("/", methods=["GET"])
|
||||
@jwt_required()
|
||||
def list_transporters():
|
||||
user_id = get_jwt_identity()
|
||||
transporters_db = Transporters()
|
||||
transporters = transporters_db.get_all_transporters_by_user(user_id)
|
||||
return jsonify(transporters), 200
|
||||
|
||||
@transporters_bp.route("/", methods=["POST"])
|
||||
@jwt_required()
|
||||
def create_transporter():
|
||||
transporters_db = Transporters()
|
||||
data = request.get_json()
|
||||
user_id = get_jwt_identity()
|
||||
transporter_id = transporters_db.create_transporter(
|
||||
user_id=user_id,
|
||||
name=data.get("name"),
|
||||
address=data.get("address"),
|
||||
register_number=data.get("register_number"),
|
||||
contact_person=data.get("contact_person"),
|
||||
phone=data.get("phone"),
|
||||
email=data.get("email"),
|
||||
vat = data.get("vat")
|
||||
)
|
||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||
return jsonify(transporter), 201
|
||||
|
||||
@transporters_bp.route("/<int:transporter_id>", methods=["PUT"])
|
||||
@jwt_required()
|
||||
def update_transporter(transporter_id):
|
||||
transporters_db = Transporters()
|
||||
user_id = get_jwt_identity()
|
||||
data = request.get_json()
|
||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||
if not transporter:
|
||||
return jsonify({"error": "Transporter not found"}), 404
|
||||
transporters_db.update_transporter(
|
||||
transporter_id=transporter_id,
|
||||
name=data.get("name"),
|
||||
address=data.get("address"),
|
||||
register_number=data.get("register_number"),
|
||||
contact_person=data.get("contact_person"),
|
||||
phone=data.get("phone"),
|
||||
email=data.get("email"),
|
||||
vat=data.get("vat")
|
||||
)
|
||||
updated_transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||
return jsonify(updated_transporter), 200
|
||||
|
||||
@transporters_bp.route("/<int:transporter_id>", methods=["DELETE"])
|
||||
@jwt_required()
|
||||
def delete_transporter(transporter_id):
|
||||
transporters_db = Transporters()
|
||||
user_id = get_jwt_identity()
|
||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||
if not transporter:
|
||||
return jsonify({"error": "Transporter not found"}), 404
|
||||
transporters_db.delete_transporter(transporter_id)
|
||||
return jsonify({"message": "Transporter deleted"}), 200
|
||||
|
||||
@transporters_bp.route("/<int:transporter_id>", methods=["GET"])
|
||||
@jwt_required()
|
||||
def get_transporter(transporter_id):
|
||||
transporters_db = Transporters()
|
||||
transporter = transporters_db.get_transporter_by_id(transporter_id)
|
||||
if not transporter:
|
||||
return jsonify({"error": "Transporter not found"}), 404
|
||||
return jsonify(transporter), 200
|
||||
Reference in New Issue
Block a user