diff --git a/app/routes/file_report.py b/app/routes/file_report.py index e48f87d..f533868 100644 --- a/app/routes/file_report.py +++ b/app/routes/file_report.py @@ -4,6 +4,7 @@ from flask import Blueprint, render_template, request, send_file, flash, jsonify from app.utils.helpers import login_required from app.utils.regex_utils import RegularExpression from app import db +from sqlalchemy import func import re from app.models.subcontractor_model import Subcontractor @@ -22,35 +23,165 @@ from app.services.abstract_service import AbstractReportService # --- BLUEPRINT DEFINITION --- file_report_bp = Blueprint("file_report", __name__, url_prefix="/file") - + + +# ---------------- LOCATION HELPERS ---------------- +WORK_MODELS = [TrenchExcavation, ManholeExcavation, ManholeDomesticChamber, Laying] + + +def get_distinct_locations(): + """Union of distinct, non-empty Location values across all 4 work tables.""" + locations = set() + for Model in WORK_MODELS: + rows = db.session.query(Model.Location).distinct().all() + for (loc,) in rows: + if loc and loc.strip(): + locations.add(loc.strip()) + return sorted(locations) + + +def get_subcontractors_for_location(location): + """Return Subcontractor objects that have at least one work record + (in any of the 4 category tables) at the given location. If no + location is given, returns every subcontractor. Shared by the AJAX + endpoint below and by the server-rendered dropdown so the list is + correct even before/without JS running (e.g. on page reload or a + validation-error re-render). + + Matching is case- and whitespace-insensitive, since Location is a + free-text field and stored values can drift ("Pune" vs "PUNE " etc.) + even though the dropdown options themselves come from a distinct + query and look identical.""" + location = (location or "").strip() + + if not location: + return Subcontractor.query.order_by(Subcontractor.subcontractor_name).all() + + target = location.upper() + sc_ids = set() + for Model in WORK_MODELS: + rows = ( + db.session.query(Model.subcontractor_id) + .filter(func.upper(func.trim(Model.Location)) == target) + .distinct() + .all() + ) + for (sid,) in rows: + if sid: + sc_ids.add(sid) + + if not sc_ids: + return [] + + return ( + Subcontractor.query.filter(Subcontractor.id.in_(sc_ids)) + .order_by(Subcontractor.subcontractor_name) + .all() + ) + + +@file_report_bp.route("/get_subcontractors_by_location") +@login_required +def get_subcontractors_by_location(): + """AJAX endpoint: return subcontractors that have at least one work + record (in any of the 4 category tables) at the given location.""" + location = request.args.get("location", "").strip() + subs = get_subcontractors_for_location(location) + + return jsonify([{"id": s.id, "name": s.subcontractor_name} for s in subs]) + + # ---------------- ACTION COLUMN ---------------- def add_action_columns(df, model_key): if df.empty: return df - # Edit + Delete side by side in one "Action" column, both as icon buttons. - df.insert(0, "Action", df["Id"].apply( - lambda x: ( - f'
| cells. jQuery DataTables then tries to
+ initialize on a table with no columns and throws, which (since the
+ init code runs as one synchronous block) silently kills every bit of
+ JS registered after it - including the select-all checkbox handler
+ for the OTHER tables on the page. Returning a plain message instead
+ of an empty |
|---|