Added Customer Name column to the Client's page and included the ability to search/filter based on it.

This commit is contained in:
rocketdebris 2025-11-11 15:32:34 -05:00
parent 4e1fbeefea
commit 8fa55bea70
2 changed files with 39 additions and 18 deletions

View File

@ -9,7 +9,7 @@ def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=N
# Assuming you have a date field to filter by - adjust the field name as needed # Assuming you have a date field to filter by - adjust the field name as needed
# Common options: creation, modified, custom_date_field, etc. # Common options: creation, modified, custom_date_field, etc.
base_filters["creation"] = ["between", [week_start_date, week_end_date]] base_filters["creation"] = ["between", [week_start_date, week_end_date]]
# Helper function to merge base filters with status filters # Helper function to merge base filters with status filters
def get_filters(status_field, status_value): def get_filters(status_field, status_value):
filters = {status_field: status_value} filters = {status_field: status_value}
@ -22,35 +22,35 @@ def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=N
"in_progress": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled", "In Progress")), "in_progress": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled", "In Progress")),
"completed": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled", "Completed")) "completed": frappe.db.count("Address", filters=get_filters("custom_onsite_meeting_scheduled", "Completed"))
} }
estimate_sent_status_counts = { estimate_sent_status_counts = {
"label": "Estimate Sent", "label": "Estimate Sent",
"not_started": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Not Started")), "not_started": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Not Started")),
"in_progress": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "In Progress")), "in_progress": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "In Progress")),
"completed": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Completed")) "completed": frappe.db.count("Address", filters=get_filters("custom_estimate_sent_status", "Completed"))
} }
job_status_counts = { job_status_counts = {
"label": "Job Status", "label": "Job Status",
"not_started": frappe.db.count("Address", filters=get_filters("custom_job_status", "Not Started")), "not_started": frappe.db.count("Address", filters=get_filters("custom_job_status", "Not Started")),
"in_progress": frappe.db.count("Address", filters=get_filters("custom_job_status", "In Progress")), "in_progress": frappe.db.count("Address", filters=get_filters("custom_job_status", "In Progress")),
"completed": frappe.db.count("Address", filters=get_filters("custom_job_status", "Completed")) "completed": frappe.db.count("Address", filters=get_filters("custom_job_status", "Completed"))
} }
payment_received_status_counts = { payment_received_status_counts = {
"label": "Payment Received", "label": "Payment Received",
"not_started": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Not Started")), "not_started": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Not Started")),
"in_progress": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "In Progress")), "in_progress": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "In Progress")),
"completed": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Completed")) "completed": frappe.db.count("Address", filters=get_filters("custom_payment_received_status", "Completed"))
} }
status_dicts = [ status_dicts = [
onsite_meeting_scheduled_status_counts, onsite_meeting_scheduled_status_counts,
estimate_sent_status_counts, estimate_sent_status_counts,
job_status_counts, job_status_counts,
payment_received_status_counts payment_received_status_counts
] ]
categories = [] categories = []
for status_dict in status_dicts: for status_dict in status_dicts:
category = { category = {
@ -74,7 +74,7 @@ def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=N
] ]
} }
categories.append(category) categories.append(category)
return categories return categories
@frappe.whitelist() @frappe.whitelist()
@ -87,7 +87,7 @@ def get_client(client_name):
["custom_address", "=", address.address_title] ["custom_address", "=", address.address_title]
]] ]]
]) ])
projects = [frappe.get_doc("Project", proj["name"]) for proj in project_names] projects = [frappe.get_doc("Project", proj["name"]) for proj in project_names]
customer = frappe.get_doc("Customer", customer_name) customer = frappe.get_doc("Customer", customer_name)
# get all associated data as needed # get all associated data as needed
@ -96,7 +96,7 @@ def get_client(client_name):
"customer": customer, "customer": customer,
"projects": projects "projects": projects
} }
@frappe.whitelist() @frappe.whitelist()
def get_clients_table_data(options): def get_clients_table_data(options):
@ -115,6 +115,7 @@ def get_clients_table_data(options):
# Map frontend field names to backend field names # Map frontend field names to backend field names
def map_field_name(frontend_field): def map_field_name(frontend_field):
field_mapping = { field_mapping = {
"customerName": "customer_name",
"addressTitle": "address_title", "addressTitle": "address_title",
"addressName": "address_title", # Legacy support "addressName": "address_title", # Legacy support
"appointmentScheduledStatus": "address_title", # These are computed fields, sort by address_title "appointmentScheduledStatus": "address_title", # These are computed fields, sort by address_title
@ -131,6 +132,10 @@ def get_clients_table_data(options):
if isinstance(filter_obj, dict) and "value" in filter_obj: if isinstance(filter_obj, dict) and "value" in filter_obj:
if filter_obj["value"] is not None and filter_obj["value"] != "": if filter_obj["value"] is not None and filter_obj["value"] != "":
# Map frontend field names to backend field names # Map frontend field names to backend field names
if field_name == "customer_name":
mapped_field_name = "custom_customer_to_bill"
else:
mapped_field_name = field_name
# Handle different match modes # Handle different match modes
match_mode = filter_obj.get("matchMode", "contains") match_mode = filter_obj.get("matchMode", "contains")
@ -138,16 +143,16 @@ def get_clients_table_data(options):
match_mode = match_mode.lower() match_mode = match_mode.lower()
if match_mode in ("contains", "contains"): if match_mode in ("contains", "contains"):
processed_filters[field_name] = ["like", f"%{filter_obj['value']}%"] processed_filters[mapped_field_name] = ["like", f"%{filter_obj['value']}%"]
elif match_mode in ("startswith", "startsWith"): elif match_mode in ("startswith", "startsWith"):
processed_filters[field_name] = ["like", f"{filter_obj['value']}%"] processed_filters[mapped_field_name] = ["like", f"{filter_obj['value']}%"]
elif match_mode in ("endswith", "endsWith"): elif match_mode in ("endswith", "endsWith"):
processed_filters[field_name] = ["like", f"%{filter_obj['value']}"] processed_filters[mapped_field_name] = ["like", f"%{filter_obj['value']}"]
elif match_mode in ("equals", "equals"): elif match_mode in ("equals", "equals"):
processed_filters[field_name] = filter_obj["value"] processed_filters[mapped_field_name] = filter_obj["value"]
else: else:
# Default to contains # Default to contains
processed_filters[field_name] = ["like", f"%{filter_obj['value']}%"] processed_filters[mapped_field_name] = ["like", f"%{filter_obj['value']}%"]
# Process sorting # Process sorting
order_by = None order_by = None
@ -173,17 +178,18 @@ def get_clients_table_data(options):
addresses = frappe.db.get_all( addresses = frappe.db.get_all(
"Address", "Address",
fields=["name", "address_title", "custom_onsite_meeting_scheduled", "custom_estimate_sent_status", "custom_job_status", "custom_payment_received_status"], fields=["name", "custom_customer_to_bill", "address_title", "custom_onsite_meeting_scheduled", "custom_estimate_sent_status", "custom_job_status", "custom_payment_received_status"],
filters=processed_filters, filters=processed_filters,
limit=options["page_size"], limit=options["page_size"],
start=(options["page"] - 1) * options["page_size"], start=(options["page"] - 1) * options["page_size"],
order_by=order_by order_by=order_by
) )
rows = [] rows = []
for address in addresses: for address in addresses:
tableRow = {} tableRow = {}
tableRow["id"] = address["name"] tableRow["id"] = address["name"]
tableRow["customer_name"] = address["custom_customer_to_bill"]
tableRow["address_title"] = address["address_title"] tableRow["address_title"] = address["address_title"]
tableRow["appointment_scheduled_status"] = address["custom_onsite_meeting_scheduled"] tableRow["appointment_scheduled_status"] = address["custom_onsite_meeting_scheduled"]
tableRow["estimate_sent_status"] = address["custom_estimate_sent_status"] tableRow["estimate_sent_status"] = address["custom_estimate_sent_status"]

View File

@ -102,12 +102,20 @@ const refreshStatusCounts = async () => {
}; };
const filters = { const filters = {
customerName: { value: null, matchMode: FilterMatchMode.CONTAINS },
addressTitle: { value: null, matchMode: FilterMatchMode.CONTAINS }, addressTitle: { value: null, matchMode: FilterMatchMode.CONTAINS },
}; };
const columns = [ const columns = [
{ {
label: "Name", label: "Customer Name",
fieldName: "customerName",
type: "text",
sortable: true,
filterable: true
},
{
label: "Address",
fieldName: "addressTitle", fieldName: "addressTitle",
type: "text", type: "text",
sortable: true, sortable: true,
@ -131,7 +139,12 @@ const columns = [
type: "status", type: "status",
sortable: true, sortable: true,
}, },
{ label: "Job Status", fieldName: "jobStatus", type: "status", sortable: true }, {
label: "Job Status",
fieldName: "jobStatus",
type: "status",
sortable: true
},
]; ];
const tableActions = [ const tableActions = [
@ -271,6 +284,8 @@ const handleLazyLoad = async (event) => {
// Call API with pagination, filters, and sorting // Call API with pagination, filters, and sorting
const result = await Api.getPaginatedClientDetails(paginationParams, filters, sorting); const result = await Api.getPaginatedClientDetails(paginationParams, filters, sorting);
console.log(result);
// Update local state - extract from pagination structure // Update local state - extract from pagination structure
tableData.value = result.data; tableData.value = result.data;
totalRecords.value = result.pagination.total; totalRecords.value = result.pagination.total;