diff --git a/custom_ui/api/db/jobs.py b/custom_ui/api/db/jobs.py index 13d8110..6e5678f 100644 --- a/custom_ui/api/db/jobs.py +++ b/custom_ui/api/db/jobs.py @@ -1,10 +1,69 @@ import frappe, json -from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response +from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response # =============================================================================== # JOB MANAGEMENT API METHODS # =============================================================================== +@frappe.whitelist() +def get_job(job_id=""): + """Get particular Job from DB""" + print("DEBUG: Loading Job from database:", job_id) + try: + project = frappe.get_doc("Project", job_id) + return build_success_response(project) + except Exception as e: + return build_error_response(str(e), 500) + + +@frappe.whitelist() +def get_job_task_table_data(filters={}, sortings={}, page=1, page_size=10): + """Get paginated job tasks table data with filtering and sorting support.""" + print("DEBUG: raw task options received:", filters, sortings, page, page_size) + + processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size) + + if is_or: + count = frappe.db.sql(*get_count_or_filters("Task", processed_filters))[0][0] + else: + count = frappe.db.count("Task", filters=processed_filters) + + print(f"DEBUG: Number of tasks returned: {count}") + + tasks = frappe.db.get_all( + "Task", + fields=["*"], + filters=processed_filters if not is_or else None, + or_filters=processed_filters if is_or else None, + limit=page_size, + start=(page - 1) * page_size, + order_by=processed_sortings + ) + + tableRows = [] + for task in tasks: + tableRow = {} + tableRow["name"] = task["name"] + tableRow["subject"] = task["subject"] + tableRow["address"] = task.get("custom_property", "") + tableRow["status"] = task.get("status", "") + tableRows.append(tableRow) + + table_data_dict = build_datatable_dict(data=tableRows, count=count, page=page, page_size=page_size) + return build_success_response(table_data_dict) + + +@frappe.whitelist() +def get_job_task_list(job_id=""): + if job_id: + try: + tasks = frappe.get_all('Task', filters={"project": job_id}) + task_docs = {task_id: frappe.get_doc(task_id) for task_id in tasks} + return build_success_response(task_docs) + except Exception as e: + return build_error_response(str(e), 500) + + @frappe.whitelist() def get_jobs_table_data(filters={}, sortings=[], page=1, page_size=10): """Get paginated job table data with filtering and sorting support.""" @@ -49,19 +108,19 @@ def upsert_job(data): try: if isinstance(data, str): data = json.loads(data) - + project_id = data.get("id") if not project_id: return {"status": "error", "message": "Project ID is required"} - + project = frappe.get_doc("Project", project_id) - + if "scheduledDate" in data: project.expected_start_date = data["scheduledDate"] - + if "foreman" in data: project.custom_install_crew = data["foreman"] - + project.save() return {"status": "success", "data": project.as_dict()} except Exception as e: @@ -75,16 +134,16 @@ def get_install_projects(start_date=None, end_date=None): # If date range provided, we could filter, but for now let's fetch all open/active ones # or maybe filter by status not Closed/Completed if we want active ones. # The user said "unscheduled" are those with status "Open" (and no date). - + projects = frappe.get_all("Project", fields=["*"], filters=filters) - + calendar_events = [] for project in projects: # Determine status status = "unscheduled" if project.get("expected_start_date"): status = "scheduled" - + # Map to calendar event format event = { "id": project.name, @@ -101,9 +160,9 @@ def get_install_projects(start_date=None, end_date=None): "notes": project.notes, "address": project.custom_installation_address } - + calendar_events.append(event) - + return {"status": "success", "data": calendar_events} except Exception as e: - return {"status": "error", "message": str(e)} \ No newline at end of file + return {"status": "error", "message": str(e)} diff --git a/frontend/src/api.js b/frontend/src/api.js index 78e7aee..c53191e 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -13,9 +13,10 @@ const FRAPPE_SEND_ESTIMATE_EMAIL_METHOD = "custom_ui.api.db.estimates.send_estim const FRAPPE_LOCK_ESTIMATE_METHOD = "custom_ui.api.db.estimates.lock_estimate"; const FRAPPE_ESTIMATE_UPDATE_RESPONSE_METHOD = "custom_ui.api.db.estimates.manual_response"; // Job methods +const FRAPPE_GET_JOB_METHOD = "custom_ui.api.db.jobs.get_job"; const FRAPPE_GET_JOBS_METHOD = "custom_ui.api.db.jobs.get_jobs_table_data"; const FRAPPE_UPSERT_JOB_METHOD = "custom_ui.api.db.jobs.upsert_job"; -const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.get_job_task_list"; +const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.jobs.get_job_task_table_data"; const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_projects"; // Invoice methods const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data"; @@ -273,6 +274,14 @@ class Api { return result; } + static async getJob(jobName) { + if (frappe.db.exists("Project", jobName)) { + const result = await this.request(FRAPPE_GET_JOB_METHOD, { jobId: jobName }) + console.log(`DEBUG: API - retrieved Job ${jobName}:`, result); + return result; + } + } + static async createJob(jobData) { const payload = DataUtils.toSnakeCaseObject(jobData); const result = await this.request(FRAPPE_UPSERT_JOB_METHOD, { data: payload }); @@ -282,15 +291,39 @@ class Api { static async getJobTaskList(jobName) { if (frappe.db.exists("Project", jobName)) { - const result = await request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { data: jobName }) + const result = await this.request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { jobId: jobName }) console.log(`DEBUG: API - retrieved task list from job ${jobName}:`, result); - return result + return result; } else { console.log(`DEBUG: API - no record found for task like from job ${jobName}: `, result); } } + static async getPaginatedJobTaskDetails(paginationParams = {}, filters = {}, sorting = null) { + const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams; + + // Use sorting from the dedicated sorting parameter first, then fall back to pagination params + const actualSortField = sorting?.field || sortField; + const actualSortOrder = sorting?.order || sortOrder; + + const options = { + page: page + 1, // Backend expects 1-based pages + page_size: pageSize, + filters, + sorting: + actualSortField && actualSortOrder + ? `${actualSortField} ${actualSortOrder === -1 ? "desc" : "asc"}` + : null, + for_table: true, + }; + + console.log("DEBUG: API - Sending job task options to backend:", options); + + const result = await this.request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { options }); + return result; + } + // ============================================================================ diff --git a/frontend/src/components/pages/Job.vue b/frontend/src/components/pages/Job.vue index 60799c7..c88d52f 100644 --- a/frontend/src/components/pages/Job.vue +++ b/frontend/src/components/pages/Job.vue @@ -5,15 +5,39 @@