diff --git a/custom_ui/api/db.py b/custom_ui/api/db.py
index fd72f76..c9702b5 100644
--- a/custom_ui/api/db.py
+++ b/custom_ui/api/db.py
@@ -1,6 +1,6 @@
import frappe, json, re
from datetime import datetime, date
-from custom_ui.db_utils import process_filters, process_sorting
+from custom_ui.db_utils import process_query_conditions, build_datatable_response, get_count_or_filters
@frappe.whitelist()
def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=None):
@@ -81,13 +81,11 @@ def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=N
@frappe.whitelist()
def get_client(client_name):
address = frappe.get_doc("Address", client_name)
- customer_name = [link for link in address.links if link.link_doctype == "Customer"][0].link_name
- project_names = frappe.db.get_all("Project", fields=["name"], filters=[
- ["or", [
- ["custom_installation_address", "=", address.address_title],
- ["custom_address", "=", address.address_title]
- ]]
- ])
+ customer_name = address.custom_customer_to_bill if address.custom_customer_to_bill else [link.link_name for link in address.links if link.link_doctype == "Customer"][0] if address.links else None
+ project_names = frappe.db.get_all("Project", fields=["name"], or_filters=[
+ ["custom_installation_address", "=", address.address_title],
+ ["custom_address", "=", address.address_title]
+ ], limit_page_length=100)
projects = [frappe.get_doc("Project", proj["name"]) for proj in project_names]
projects_data = []
@@ -101,47 +99,43 @@ def get_client(client_name):
@frappe.whitelist()
-def get_clients_table_data(filters = {}, sorting = [], page=1, page_size=10):
- print("DEBUG: Raw client table options received:", {
+def get_clients_table_data(filters = {}, sortings = [], page=1, page_size=10):
+ print("DEBUG: Raw client table query received:", {
"filters": filters,
- "sorting": sorting,
+ "sortings": sortings,
"page": page,
"page_size": page_size
})
- page = int(page)
- page_size = int(page_size)
- processed_filters = process_filters(filters)
- print("DEBUG: Processed filters:", processed_filters)
-
-
- processed_sortings = process_sorting(sorting)
- print("DEBUG: Order by:", processed_sortings)
-
- count = frappe.db.count("Address", filters=processed_filters)
- print("DEBUG: Total addresses count:", count)
- print("DEBUG: Filters (repr):", repr(processed_filters), "Type:", type(processed_filters))
- print("DEBUG: Order by (repr):", repr(processed_sortings), "Type:", type(processed_sortings))
+ processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)
+ count = frappe.db.count(
+ "Address",
+ filters=processed_filters
+ ) if not is_or else frappe.db.sql(
+ *get_count_or_filters("Address", processed_filters)
+ )[0][0]
+ print("DEBUG: Count of addresses matching filters:", count)
address_names = frappe.db.get_all(
"Address",
fields=["name"],
- filters=processed_filters if isinstance(processed_filters, dict) else None,
- or_filters=processed_filters if isinstance(processed_filters, list) else None,
+ 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
)
- print("DEBUG: Retrieved address names:", address_names)
-
addresses = [frappe.get_doc("Address", addr["name"]).as_dict() for addr in address_names]
-
- rows = []
+ tableRows = []
for address in addresses:
- links = address.links
- print("DEBUG: Address links:", links)
- customer_links = [link for link in links if link.link_doctype == "Customer"] if links else None
- print("DEBUG: Extracted customer links:", customer_links)
- customer_name = address["custom_customer_to_bill"] if address.get("custom_customer_to_bill") else (customer_links[0].link_name if customer_links else "N/A")
tableRow = {}
+ links = address.links
+ customer_links = [link for link in links if link.link_doctype == "Customer"] if links else None
+ customer_name = address.get("custom_customer_to_bill")
+ if not customer_name and not customer_links:
+ print("DEBUG: No customer links found and no customer to bill.")
+ customer_name = "N/A"
+ elif not customer_name and customer_links:
+ print("DEBUG: No customer to bill. Customer links found:", customer_links)
+ customer_name = customer_links[0].link_name
tableRow["id"] = address["name"]
tableRow["customer_name"] = customer_name
tableRow["address"] = (
@@ -153,17 +147,8 @@ def get_clients_table_data(filters = {}, sorting = [], page=1, page_size=10):
tableRow["estimate_sent_status"] = address.custom_estimate_sent_status
tableRow["job_status"] = address.custom_job_status
tableRow["payment_received_status"] = address.custom_payment_received_status
- rows.append(tableRow)
-
- return {
- "pagination": {
- "total": count,
- "page": page,
- "page_size": page_size,
- "total_pages": (count + page_size - 1) // page_size
- },
- "data": rows
- }
+ tableRows.append(tableRow)
+ return build_datatable_response(data=tableRows, count=count, page=page, page_size=page_size)
@frappe.whitelist()
@@ -215,6 +200,7 @@ def upsert_client(data):
"country": "United States",
"address_title": data.get("address_title"),
"pincode": data.get("pincode"),
+ "custom_customer_to_bill": customer_doc.name
}).insert(ignore_permissions=True)
link = {
"link_doctype": "Customer",
@@ -230,197 +216,58 @@ def upsert_client(data):
}
@frappe.whitelist()
-def get_jobs(options):
- options = json.loads(options)
- print("DEBUG: Raw job options received:", options)
- defaultOptions = {
- "fields": ["*"],
- "filters": {},
- "sorting": {},
- "page": 1,
- "page_size": 10,
- "for_table": False
- }
- options = {**defaultOptions, **options}
- print("DEBUG: Final job options:", options)
-
- jobs = []
- tableRows = []
-
- # Process filters from PrimeVue format to Frappe format
- processed_filters = {}
- if options["filters"]:
- for field_name, filter_obj in options["filters"].items():
- if isinstance(filter_obj, dict) and "value" in filter_obj:
- if filter_obj["value"] is not None and filter_obj["value"] != "":
- # Map frontend field names to backend field names
- backend_field = map_job_field_name(field_name)
-
- # Handle different match modes
- match_mode = filter_obj.get("matchMode", "contains")
- if isinstance(match_mode, str):
- match_mode = match_mode.lower()
-
- if match_mode in ("contains", "contains"):
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}%"]
- elif match_mode in ("startswith", "startsWith"):
- processed_filters[backend_field] = ["like", f"{filter_obj['value']}%"]
- elif match_mode in ("endswith", "endsWith"):
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}"]
- elif match_mode in ("equals", "equals"):
- processed_filters[backend_field] = filter_obj["value"]
- else:
- # Default to contains
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}%"]
-
- # Process sorting
- order_by = None
- if options.get("sorting") and options["sorting"]:
- sorting_str = options["sorting"]
- if sorting_str and sorting_str.strip():
- # Parse "field_name asc/desc" format
- parts = sorting_str.strip().split()
- if len(parts) >= 2:
- sort_field = parts[0]
- sort_direction = parts[1].lower()
- # Map frontend field to backend field
- backend_sort_field = map_job_field_name(sort_field)
- order_by = f"{backend_sort_field} {sort_direction}"
-
- print("DEBUG: Processed job filters:", processed_filters)
- print("DEBUG: Job order by:", order_by)
-
- count = frappe.db.count("Project", filters=processed_filters)
- print("DEBUG: Total projects count:", count)
-
+def get_jobs_table_data(filters = {}, sortings = [], page=1, page_size=10):
+ print("DEBUG: Raw job options received:", filters, sortings, page, page_size)
+ processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)
+ count = frappe.db.count(
+ "Project",
+ filters=processed_filters if not is_or else None,
+ or_filters=processed_filters if is_or else None,
+ ) if not is_or else frappe.db.sql(
+ build
+ )
projects = frappe.db.get_all(
"Project",
- fields=options["fields"],
- filters=processed_filters,
- limit=options["page_size"],
- start=(options["page"] - 1) * options["page_size"],
- order_by=order_by
+ 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 project in projects:
- job = {}
tableRow = {}
-
tableRow["id"] = project["name"]
tableRow["name"] = project["name"]
- tableRow["customInstallationAddress"] = project.get("custom_installation_address", "")
+ tableRow["installation_address"] = project.get("custom_installation_address", "")
tableRow["customer"] = project.get("customer", "")
tableRow["status"] = project.get("status", "")
- tableRow["percentComplete"] = project.get("percent_complete", 0)
- tableRows.append(tableRow)
-
- job["project"] = project
- jobs.append(job)
-
- return {
- "pagination": {
- "total": count,
- "page": options["page"],
- "page_size": options["page_size"],
- "total_pages": (count + options["page_size"] - 1) // options["page_size"]
- },
- "data": tableRows if options["for_table"] else jobs
- }
+ tableRow["percent_complete"] = project.get("percent_complete", 0)
+ tableRows.append(tableRow)
+ return build_datatable_response(data=tableRows, count=count, page=page, page_size=page_size)
@frappe.whitelist()
-def get_warranty_claims(options):
- options = json.loads(options)
- print("DEBUG: Raw warranty options received:", options)
- defaultOptions = {
- "fields": ["*"],
- "filters": {},
- "sorting": {},
- "page": 1,
- "page_size": 10,
- "for_table": False
- }
- options = {**defaultOptions, **options}
- print("DEBUG: Final warranty options:", options)
-
- warranties = []
- tableRows = []
-
- # Map frontend field names to backend field names for Warranty Claim doctype
- def map_warranty_field_name(frontend_field):
- field_mapping = {
- "warrantyId": "name",
- "customer": "customer_name",
- "serviceAddress": "service_address",
- "complaint": "complaint",
- "status": "status",
- "complaintDate": "complaint_date",
- "complaintRaisedBy": "complaint_raised_by",
- "fromCompany": "from_company",
- "territory": "territory",
- "resolutionDate": "resolution_date",
- "warrantyStatus": "warranty_amc_status"
- }
- return field_mapping.get(frontend_field, frontend_field)
-
- # Process filters from PrimeVue format to Frappe format
- processed_filters = {}
- if options["filters"]:
- for field_name, filter_obj in options["filters"].items():
- if isinstance(filter_obj, dict) and "value" in filter_obj:
- if filter_obj["value"] is not None and filter_obj["value"] != "":
- # Map frontend field names to backend field names
- backend_field = map_warranty_field_name(field_name)
-
- # Handle different match modes
- match_mode = filter_obj.get("matchMode", "contains")
- if isinstance(match_mode, str):
- match_mode = match_mode.lower()
-
- if match_mode in ("contains", "contains"):
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}%"]
- elif match_mode in ("startswith", "startsWith"):
- processed_filters[backend_field] = ["like", f"{filter_obj['value']}%"]
- elif match_mode in ("endswith", "endsWith"):
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}"]
- elif match_mode in ("equals", "equals"):
- processed_filters[backend_field] = filter_obj["value"]
- else:
- # Default to contains
- processed_filters[backend_field] = ["like", f"%{filter_obj['value']}%"]
-
- # Process sorting
- order_by = None
- if options.get("sorting") and options["sorting"]:
- sorting_str = options["sorting"]
- if sorting_str and sorting_str.strip():
- # Parse "field_name asc/desc" format
- parts = sorting_str.strip().split()
- if len(parts) >= 2:
- sort_field = parts[0]
- sort_direction = parts[1].lower()
- # Map frontend field to backend field
- backend_sort_field = map_warranty_field_name(sort_field)
- order_by = f"{backend_sort_field} {sort_direction}"
-
- print("DEBUG: Processed warranty filters:", processed_filters)
- print("DEBUG: Warranty order by:", order_by)
-
- count = frappe.db.count("Warranty Claim", filters=processed_filters)
- print("DEBUG: Total warranty claims count:", count)
-
+def get_warranty_claims(filters = {}, sortings = [], page=1, page_size=10):
+ print("DEBUG: Raw warranty options received:", filters, sortings, page, page_size)
+ processed_filters, processed_sortings, is_or, page, page_size = process_query_conditions(filters, sortings, page, page_size)
+ count = frappe.db.count(
+ "Warranty Claim",
+ filters=processed_filters if not is_or else None,
+ or_filters=processed_filters if is_or else None
+ )
warranty_claims = frappe.db.get_all(
"Warranty Claim",
- fields=options["fields"],
- filters=processed_filters,
- limit=options["page_size"],
- start=(options["page"] - 1) * options["page_size"],
- order_by=order_by
+ 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 warranty in warranty_claims:
- warranty_obj = {}
tableRow = {}
-
tableRow["id"] = warranty["name"]
tableRow["warrantyId"] = warranty["name"]
tableRow["customer"] = warranty.get("customer_name", "")
@@ -467,18 +314,5 @@ def get_warranty_claims(options):
tableRow["priority"] = "Medium"
else:
tableRow["priority"] = "Low"
-
tableRows.append(tableRow)
-
- warranty_obj["warranty_claim"] = warranty
- warranties.append(warranty_obj)
-
- return {
- "pagination": {
- "total": count,
- "page": options["page"],
- "page_size": options["page_size"],
- "total_pages": (count + options["page_size"] - 1) // options["page_size"]
- },
- "data": tableRows if options["for_table"] else warranties
- }
+ return build_datatable_response(data=tableRows, count=count, page=page, page_size=page_size)
\ No newline at end of file
diff --git a/custom_ui/db_utils.py b/custom_ui/db_utils.py
index 52bb1d3..80b9dd3 100644
--- a/custom_ui/db_utils.py
+++ b/custom_ui/db_utils.py
@@ -10,6 +10,10 @@ def map_field_name(frontend_field):
"payment_received_status": "custom_payment_received_status",
"job_status": "custom_job_status",
"installation_address": "custom_installation_address",
+ "warranty_id": "name",
+ "customer": "customer_name",
+ "fromCompany": "from_company",
+ "warranty_status": "warranty_amc_status"
}
return field_mapping.get(frontend_field, frontend_field)
@@ -58,6 +62,7 @@ def process_filters(filters):
else:
# Default to contains
processed_filters[mapped_field_name] = ["like", f"%{filter_obj['value']}%"]
+ print("DEBUG: Processed filters:", processed_filters)
return processed_filters
def process_sorting(sortings):
@@ -71,4 +76,40 @@ def process_sorting(sortings):
order_by = order_by.rstrip(", ")
else:
order_by = "modified desc"
- return order_by
\ No newline at end of file
+ print("DEBUG: Processed sorting:", order_by)
+ return order_by
+
+
+def process_query_conditions(filters, sortings, page, page_size):
+ processed_filters = process_filters(filters)
+ processed_sortings = process_sorting(sortings)
+ is_or_filters = isinstance(processed_filters, list)
+ page_int = int(page)
+ page_size_int = int(page_size)
+ return processed_filters, processed_sortings, is_or_filters, page_int, page_size_int
+
+
+def build_datatable_response(data, count, page, page_size):
+ return {
+ "pagination": {
+ "total": count,
+ "page": page,
+ "page_size": page_size,
+ "total_pages": (count + page_size - 1) // page_size
+ },
+ "data": data
+ }
+
+def get_count_or_filters(doctype, or_filters):
+ where_clauses = []
+ values = []
+ for field, operator, val in or_filters:
+ if operator.lower() == "like":
+ where_clauses.append(f"`{field}` LIKE %s")
+ else:
+ where_clauses.append(f"`{field}` {operator} %s")
+ values.append(val)
+ where_sql = " OR ".join(where_clauses)
+ sql = f"SELECT COUNT(*) FROM `tab{doctype}` WHERE {where_sql}"
+ return sql, values
+
\ No newline at end of file
diff --git a/frontend/src/components/pages/Client.vue b/frontend/src/components/pages/Client.vue
index ab23b8e..fa5d71f 100644
--- a/frontend/src/components/pages/Client.vue
+++ b/frontend/src/components/pages/Client.vue
@@ -29,31 +29,32 @@