diff --git a/app/classes/service.py b/app/classes/service.py index 1e1f180..3b64d0c 100644 --- a/app/classes/service.py +++ b/app/classes/service.py @@ -18,30 +18,46 @@ class Service_Service(db.Model): 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()) - - class Service_Service_schema(ma.SQLAlchemyAutoSchema): class Meta: 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_Parts(db.Model): + __tablename__ = 'service_parts' + __table_args__ = {"schema": "public"} + + id = db.Column(db.Integer, + primary_key=True, + autoincrement=True, + unique=False) + + 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)) + + + +class Service_Parts_schema(ma.SQLAlchemyAutoSchema): + class Meta: + 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') \ No newline at end of file diff --git a/app/info/views.py b/app/info/views.py index f942510..a44d626 100755 --- a/app/info/views.py +++ b/app/info/views.py @@ -1,10 +1,40 @@ 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_Oil_Oil_schema from app.classes.admin import Admin_Company + + +@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 + # e.g., { 100: (2.92 * 100), 125: (2.92 * 125), ... } + pricing_totals = { + gallons: price_per_gallon * gallons + for gallons in gallon_tiers + } + + # Return the dictionary of totals + # e.g., { "100": "292.00", "125": "365.00", ... } + return jsonify(pricing_totals) + @info.route("/price/oil", methods=["GET"]) def get_oil_price_today(): get_price_query = (db.session diff --git a/app/service/views.py b/app/service/views.py index f602404..bbc2abe 100644 --- a/app/service/views.py +++ b/app/service/views.py @@ -4,109 +4,108 @@ from app import db from datetime import datetime, date from app.classes.customer import (Customer_Customer) from app.classes.service import (Service_Service, - Service_Service_schema + Service_Service_schema, Service_Parts, Service_Parts_schema ) -# --- NEW ENDPOINT TO GET ALL SERVICE CALLS FOR THE MASTER CALENDAR --- @service.route("/all", methods=["GET"]) def get_all_service_calls(): - """ - Fetches ALL service calls from the database and formats them for FullCalendar. - """ - # 1. Query all service records, without filtering by customer all_services = Service_Service.query.all() - - # 2. Reuse the same formatting logic (colors, titles, etc.) color_map = { - 0: {"backgroundColor": "blue", "textColor": "white"}, - 1: {"backgroundColor": "red", "textColor": "white"}, - 2: {"backgroundColor": "green", "textColor": "white"}, - 3: {"backgroundColor": "yellow", "textColor": "black"}, + 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') - # The title now includes the customer name, which is crucial for a master calendar 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 correctly format the date to a string serialized_record = Service_Service_schema().dump(service_record) - event_data = { - "id": service_record.id, - "title": event_title, - "start": serialized_record.get('scheduled_date'), # Use the reliable formatted date - "end": None, + "id": service_record.id, "title": event_title, "start": serialized_record.get('scheduled_date'), + "end": None, "customer_id": service_record.customer_id, "extendedProps": { "description": service_record.description, "type_service_call": service_record.type_service_call, }, - "backgroundColor": event_colors.get("backgroundColor"), - "textColor": event_colors.get("textColor"), + "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 -# --- YOUR OTHER EXISTING ROUTES (no changes needed below) --- - +# --- 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(): - # ... (no changes) + """ + 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()) + 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("/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/", 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 - - # --- FIX: Use fromisoformat to parse the FULL timestamp string (e.g., "2025-08-26T14:00:00") --- - # This correctly preserves the time sent from the frontend. 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, # Save the full datetime object - description=data.get('description'), + scheduled_date=scheduled_datetime_obj, description=data.get('description'), ) db.session.add(new_service_call) db.session.commit() return jsonify({ "ok": True, "id": new_service_call.id }), 201 - @service.route("/update/", 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 - - # --- FIX: Also use fromisoformat here to correctly update with time --- 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) - try: db.session.commit() service_schema = Service_Service_schema(many=False) @@ -114,46 +113,46 @@ def update_service_call(id): except Exception as e: db.session.rollback() return jsonify({"error": str(e)}), 500 - - -@service.route("/upcoming/count", methods=["GET"]) -def get_upcoming_service_calls_count(): - """ - Efficiently counts the number of all future service calls from today onwards. - """ - # 1. Get the current time - now = datetime.now() - - # 2. Build the query and use the database's optimized count() function +@service.route("/delete/", methods=["DELETE"]) +def delete_service_call(id): + service_record = Service_Service.query.get_or_404(id) try: - count = ( - db.session.query(Service_Service) - .filter(Service_Service.scheduled_date >= now) - .count() - ) - # 3. Return the count in a simple JSON object - return jsonify({"count": count}), 200 + db.session.delete(service_record) + db.session.commit() + return jsonify({"ok": True, "message": "Service deleted successfully"}), 200 except Exception as e: - # Return an error if the query fails + db.session.rollback() return jsonify({"error": str(e)}), 500 +@service.route("/parts/customer/", methods=["GET"]) +def get_service_parts(customer_id): + parts = Service_Parts.query.filter_by(customer_id=customer_id).first() + if parts: + parts_schema = Service_Parts_schema() + return jsonify(parts_schema.dump(parts)), 200 + else: + return jsonify({ + "customer_id": customer_id, "oil_filter": "", "oil_filter_2": "", + "oil_nozzle": "", "oil_nozzle_2": "" + }), 200 -@service.route("/for-customer/", methods=["GET"]) -def get_service_calls_for_customer(customer_id): - """ - Fetches all service calls for a specific customer, ordered by most recent first. - """ - # Query the database, filtering by customer_id and ordering by date - service_records = ( - Service_Service.query - .filter_by(customer_id=customer_id) - .order_by(Service_Service.scheduled_date.desc()) # .desc() for newest first - .all() - ) - - # Use the schema to convert the list of objects to JSON - service_schema = Service_Service_schema(many=True) - result = service_schema.dump(service_records) - - return jsonify(result), 200 \ No newline at end of file +@service.route("/parts/update/", methods=["POST"]) +def update_service_parts(customer_id): + data = request.get_json() + if not data: + return jsonify({"error": "No data provided"}), 400 + 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) + try: + 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 \ No newline at end of file