From 88171caee6c5e8ccc8e67923dba9b9839e30ef66 Mon Sep 17 00:00:00 2001 From: rocketdebris Date: Fri, 7 Nov 2025 11:46:15 -0500 Subject: [PATCH] Added Estimate Modal and solved some merge conflicts. --- custom_ui/api/db.py | 88 ++--- frontend/src/App.vue | 4 +- frontend/src/api.js | 13 +- frontend/src/components/SideBar.vue | 3 +- ...tClientModal.vue => CreateClientModal.vue} | 0 .../components/modals/CreateEstimateModal.vue | 328 ++++++++++++++++++ 6 files changed, 389 insertions(+), 47 deletions(-) rename frontend/src/components/modals/{CreatClientModal.vue => CreateClientModal.vue} (100%) create mode 100644 frontend/src/components/modals/CreateEstimateModal.vue diff --git a/custom_ui/api/db.py b/custom_ui/api/db.py index 2efe36a..64cc4a0 100644 --- a/custom_ui/api/db.py +++ b/custom_ui/api/db.py @@ -15,10 +15,10 @@ def get_clients(options): } 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 = { @@ -26,11 +26,11 @@ def get_clients(options): "addressName": "address_title", # Legacy support "appointmentScheduledStatus": "address_title", # These are computed fields, sort by address_title "estimateSentStatus": "address_title", - "paymentReceivedStatus": "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"]: @@ -39,12 +39,12 @@ def get_clients(options): 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"): @@ -56,7 +56,7 @@ def get_clients(options): 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"]: @@ -70,71 +70,71 @@ def get_clients(options): # 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"], + "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"], + "custom_installation_address": address["address_title"], "project_template": "SNW Install" } ) - + tasks = frappe.db.get_all( - "Task", - fields=["*"], + "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" @@ -142,13 +142,13 @@ def get_clients(options): 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, @@ -220,10 +220,10 @@ def get_jobs(options): } 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 = { @@ -234,7 +234,7 @@ def get_jobs(options): "percentComplete": "percent_complete" } return field_mapping.get(frontend_field, frontend_field) - + # Process filters from PrimeVue format to Frappe format processed_filters = {} if options["filters"]: @@ -243,12 +243,12 @@ def get_jobs(options): 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"): @@ -260,7 +260,7 @@ def get_jobs(options): 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"]: @@ -274,26 +274,26 @@ def get_jobs(options): # 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"], + "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", "") @@ -301,10 +301,10 @@ def get_jobs(options): 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, @@ -313,4 +313,8 @@ def get_jobs(options): "total_pages": (count + options["page_size"] - 1) // options["page_size"] }, "data": tableRows if options["for_table"] else jobs - } \ No newline at end of file + } + +@frappe.whitelist() +def upsert_estimate(): + pass diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 0e0e2bb..6f0d1ca 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,8 @@ @@ -26,6 +27,7 @@ import ScrollPanel from "primevue/scrollpanel"; + diff --git a/frontend/src/api.js b/frontend/src/api.js index 22a38c0..ce23c0b 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -233,8 +233,9 @@ class Api { customer: timesheet.customer, totalHours: timesheet.total_hours, status: timesheet.status, - totalPayFormatted: timesheet.total_costing_amount, - }; + totalPay: timesheet.total_costing_amount + } + console.log("Timesheet Row: ", tableRow); data.push(tableRow); } console.log("DEBUG: API - getTimesheetData result: ", data); @@ -311,7 +312,7 @@ class Api { * @param {Object} filters * @returns {Promise} */ - static async getDocsList(doctype, fields = [], filters = {}, page = 0, pageLength = 0) { + static async getDocsList(doctype, fields = [], filters = {}, page = 0, start=0, pageLength = 0) { const docs = await frappe.db.get_list(doctype, { fields, filters, @@ -361,6 +362,12 @@ class Api { return customerNames; } + static async getCompanyNames() { + const companies = await this.getDocsList("Company", ["name"]); + console.log("DEBUG: API - Fetched Company Names: ", companyNames); + return companyNames; + } + // Create methods static async createClient(clientData) { diff --git a/frontend/src/components/SideBar.vue b/frontend/src/components/SideBar.vue index e90fe01..d2603f1 100644 --- a/frontend/src/components/SideBar.vue +++ b/frontend/src/components/SideBar.vue @@ -46,7 +46,8 @@ const createButtons = ref([ { label: "Estimate", command: () => { - frappe.new_doc("Estimate"); + //frappe.new_doc("Estimate"); + modalStore.openModal("createEstimate"); }, }, { diff --git a/frontend/src/components/modals/CreatClientModal.vue b/frontend/src/components/modals/CreateClientModal.vue similarity index 100% rename from frontend/src/components/modals/CreatClientModal.vue rename to frontend/src/components/modals/CreateClientModal.vue diff --git a/frontend/src/components/modals/CreateEstimateModal.vue b/frontend/src/components/modals/CreateEstimateModal.vue new file mode 100644 index 0000000..d3ba7c3 --- /dev/null +++ b/frontend/src/components/modals/CreateEstimateModal.vue @@ -0,0 +1,328 @@ + + + + +