from flask import request, jsonify from app.service import service from app import db 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 ) @service.route("/all", methods=["GET"]) def get_all_service_calls(): try: 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/", 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/", 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/", 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/", 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: return jsonify(None), 200 except Exception as e: return jsonify({"error": str(e)}), 500 @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/", 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/", 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("/", 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/", 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/", 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("/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 @service.route("/payment//", methods=["PUT"]) def process_service_payment(service_id, payment_type): service = db.session.query(Service_Service).filter(Service_Service.id == service_id).first() if not service: return jsonify({"ok": False, "error": "Service not found"}), 404 # Set payment columns as specified service.payment_type = payment_type # e.g., 1 for Tiger service.payment_status = 2 # As specified # payment_card_id retains the selected card's ID if set in the service record try: db.session.commit() return jsonify({"ok": True}), 200 except Exception as e: db.session.rollback() return jsonify({"error": str(e)}), 500