321 lines
12 KiB
Python
321 lines
12 KiB
Python
import frappe, json
|
|
from custom_ui.db_utils import calculate_appointment_scheduled_status, calculate_estimate_sent_status, calculate_payment_recieved_status, calculate_job_status
|
|
|
|
@frappe.whitelist()
|
|
def get_clients(options):
|
|
options = json.loads(options)
|
|
print("DEBUG: Raw options received:", options)
|
|
defaultOptions = {
|
|
"fields": ["*"],
|
|
"filters": {},
|
|
"sorting": {},
|
|
"page": 1,
|
|
"page_size": 10,
|
|
"for_table": False
|
|
}
|
|
options = {**defaultOptions, **options}
|
|
print("DEBUG: Final options:", options)
|
|
|
|
clients = []
|
|
tableRows = []
|
|
|
|
# Map frontend field names to backend field names
|
|
def map_field_name(frontend_field):
|
|
field_mapping = {
|
|
"addressTitle": "address_title",
|
|
"addressName": "address_title", # Legacy support
|
|
"appointmentScheduledStatus": "address_title", # These are computed fields, sort by address_title
|
|
"estimateSentStatus": "address_title",
|
|
"paymentReceivedStatus": "address_title",
|
|
"jobStatus": "address_title"
|
|
}
|
|
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_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_field_name(sort_field)
|
|
order_by = f"{backend_sort_field} {sort_direction}"
|
|
|
|
print("DEBUG: Processed filters:", processed_filters)
|
|
print("DEBUG: Order by:", order_by)
|
|
|
|
count = frappe.db.count("Address", filters=processed_filters)
|
|
print("DEBUG: Total addresses count:", count)
|
|
|
|
addresses = frappe.db.get_all(
|
|
"Address",
|
|
fields=options["fields"],
|
|
filters=processed_filters,
|
|
limit=options["page_size"],
|
|
start=(options["page"] - 1) * options["page_size"],
|
|
order_by=order_by
|
|
)
|
|
|
|
for address in addresses:
|
|
client = {}
|
|
tableRow = {}
|
|
|
|
on_site_meetings = frappe.db.get_all(
|
|
"On-Site Meeting",
|
|
fields=["*"],
|
|
filters={"address": address["address_title"]}
|
|
)
|
|
|
|
quotations = frappe.db.get_all(
|
|
"Quotation",
|
|
fields=["*"],
|
|
filters={"custom_installation_address": address["address_title"]}
|
|
)
|
|
|
|
sales_orders = frappe.db.get_all(
|
|
"Sales Order",
|
|
fields=["*"],
|
|
filters={"custom_installation_address": address["address_title"]}
|
|
)
|
|
|
|
sales_invvoices = frappe.db.get_all(
|
|
"Sales Invoice",
|
|
fields=["*"],
|
|
filters={"custom_installation_address": address["address_title"]}
|
|
)
|
|
|
|
payment_entries = frappe.db.get_all(
|
|
"Payment Entry",
|
|
fields=["*"],
|
|
filters={"custom_installation_address": address["address_title"]}
|
|
)
|
|
|
|
jobs = frappe.db.get_all(
|
|
"Project",
|
|
fields=["*"],
|
|
filters={
|
|
"custom_installation_address": address["address_title"],
|
|
"project_template": "SNW Install"
|
|
}
|
|
)
|
|
|
|
tasks = frappe.db.get_all(
|
|
"Task",
|
|
fields=["*"],
|
|
filters={"project": jobs[0]["name"]}
|
|
) if jobs else []
|
|
|
|
tableRow["id"] = address["name"]
|
|
tableRow["address_title"] = address["address_title"]
|
|
tableRow["appointment_scheduled_status"] = calculate_appointment_scheduled_status(on_site_meetings[0]) if on_site_meetings else "Not Started"
|
|
tableRow["estimate_sent_status"] = calculate_estimate_sent_status(quotations[0]) if quotations else "Not Started"
|
|
tableRow["payment_received_status"] = calculate_payment_recieved_status(sales_invvoices[0], payment_entries) if sales_invvoices and payment_entries else "Not Started"
|
|
tableRow["job_status"] = calculate_job_status(jobs[0], tasks) if jobs and tasks else "Not Started"
|
|
tableRows.append(tableRow)
|
|
|
|
client["address"] = address
|
|
client["on_site_meetings"] = on_site_meetings
|
|
client["jobs"] = jobs
|
|
client["quotations"] = quotations
|
|
clients.append(client)
|
|
|
|
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 clients
|
|
}
|
|
|
|
@frappe.whitelist()
|
|
def upsert_client(data):
|
|
data = json.loads(data)
|
|
"""
|
|
Upsert a document in the database.
|
|
If a document with the same name exists, it will be updated.
|
|
Otherwise, a new document will be created.
|
|
"""
|
|
customer = frappe.db.exists("Customer", {"customer_name": data.get("customer_name")})
|
|
if not customer:
|
|
customer_doc = frappe.get_doc({
|
|
"doctype": "Customer",
|
|
"customer_name": data.get("customer_name"),
|
|
"customer_type": data.get("customer_type")
|
|
}).insert(ignore_permissions=True)
|
|
else:
|
|
customer_doc = frappe.get_doc("Customer", customer)
|
|
print("Customer:", customer_doc.as_dict())
|
|
filters = {
|
|
"address_title": data.get("address_title"),
|
|
}
|
|
existing_address = frappe.db.exists("Address", filters)
|
|
print("Existing address check:", existing_address)
|
|
if existing_address:
|
|
frappe.throw(f"Address already exists for customer {data.get('customer_name')}.", frappe.ValidationError)
|
|
address_doc = frappe.get_doc({
|
|
"doctype": "Address",
|
|
"address_line1": data.get("address_line1"),
|
|
"city": data.get("city"),
|
|
"state": data.get("state"),
|
|
"country": "United States",
|
|
"address_title": data.get("address_title"),
|
|
"pincode": data.get("pincode"),
|
|
}).insert(ignore_permissions=True)
|
|
link = {
|
|
"link_doctype": "Customer",
|
|
"link_name": customer_doc.name
|
|
}
|
|
address_doc.append("links", link)
|
|
address_doc.save(ignore_permissions=True)
|
|
|
|
return {
|
|
"customer": customer_doc,
|
|
"address": address_doc,
|
|
"success": True
|
|
}
|
|
|
|
@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 = []
|
|
|
|
# Map frontend field names to backend field names for Project doctype
|
|
def map_job_field_name(frontend_field):
|
|
field_mapping = {
|
|
"name": "name",
|
|
"customInstallationAddress": "custom_installation_address",
|
|
"customer": "customer",
|
|
"status": "status",
|
|
"percentComplete": "percent_complete"
|
|
}
|
|
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_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)
|
|
|
|
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
|
|
)
|
|
|
|
for project in projects:
|
|
job = {}
|
|
tableRow = {}
|
|
|
|
tableRow["id"] = project["name"]
|
|
tableRow["name"] = project["name"]
|
|
tableRow["customInstallationAddress"] = 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
|
|
}
|
|
|
|
@frappe.whitelist()
|
|
def upsert_estimate():
|
|
pass
|