diff --git a/custom_ui/api/db.py b/custom_ui/api/db.py index 19a5607..675d1b8 100644 --- a/custom_ui/api/db.py +++ b/custom_ui/api/db.py @@ -1,5 +1,58 @@ import frappe, json +@frappe.whitelist() +def get_clients(options): + options = json.loads(options) + defaultOptions = { + "fields": ["*"], + "filters": {}, + "sorting": {}, + "page": 1, + "page_size": 10 + } + options = {**defaultOptions, **options} + + clients = [] + + count = frappe.db.count("Address", filters=options["filters"]) + print("DEBUG: Total addresses count:", count) + + addresses = frappe.db.get_all( + "Address", + fields=options["fields"], + filters=options["filters"], + limit=options["page_size"], + start=(options["page"] - 1) * options["page_size"], + order_by=(options["sorting"]) + ) + + for address in addresses: + client = {} + print("DEBUG: Processing address:", address) + + quotations = frappe.db.get_all( + "Quotation", + fields=["*"], + filters={"custom_installation_address": address["name"]} + ) + + jobs = frappe.db.get_all("Project", + fields=["*"], + filters={"custom_installation_address": address["name"], + "project_template": "SNW Install"}) + client["address"] = address + client["on_site_meetings"] = [] + client["jobs"] = jobs + client["quotations"] = quotations + clients.append(client) + + return { + "count": count, + "page": options["page"], + "page_size": options["page_size"], + "clients": clients + } + @frappe.whitelist() def upsert_client(data): data = json.loads(data) @@ -15,17 +68,14 @@ def upsert_client(data): "customer_name": data.get("customer_name"), "customer_type": data.get("customer_type") }).insert(ignore_permissions=True) - customer = customer_doc.name else: customer_doc = frappe.get_doc("Customer", customer) + print("Customer:", customer_doc.as_dict()) filters = { - "address_line1": data.get("address_line1"), - "city": data.get("city"), - "state": data.get("state"), - "country": "US", - "pincode": data.get("pincode") + "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({ @@ -33,17 +83,19 @@ def upsert_client(data): "address_line1": data.get("address_line1"), "city": data.get("city"), "state": data.get("state"), - "country": "US", + "country": "United States", + "address_title": data.get("address_title"), "pincode": data.get("pincode"), }).insert(ignore_permissions=True) link = { "link_doctype": "Customer", - "link_name": customer + "link_name": customer_doc.name } address_doc.append("links", link) address_doc.save(ignore_permissions=True) return { - "customer": customer.name, - "address": address_doc.name + "customer": customer_doc, + "address": address_doc, + "success": True } \ No newline at end of file diff --git a/frontend/src/api.js b/frontend/src/api.js index 0d056a1..089d80f 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -7,13 +7,15 @@ const FRAPPE_UPSERT_CLIENT_METHOD = "custom_ui.api.db.upsert_client"; class Api { static async request(frappeMethod, args = {}) { + args = DataUtils.toSnakeCaseObject(args); try { - const response = await frappe.call({ + let response = await frappe.call({ method: frappeMethod, args: { ...args, }, }); + response = DataUtils.toCamelCaseObject(response); console.log("DEBUG: API - Request Response: ", response); return response.message; } catch (error) { @@ -24,39 +26,26 @@ class Api { } static async getClientDetails(options = {}) { - const { - forTable = true, - page = 0, - pageSize = 10, - filters = {}, - sortField = null, - sortOrder = null, - searchTerm = null, - } = options; + return await this.request("custom_ui.api.db.get_clients", { options }); - console.log("DEBUG: API - getClientDetails called with options:", options); - - // Build filters for the Address query - let addressFilters = {}; - - // Add search functionality across multiple fields - if (searchTerm) { - // Note: This is a simplified version. You might want to implement - // full-text search on the backend for better performance + // Handle fullName filter by searching in multiple fields + // Since fullName is constructed from customer_name + address_line1 + city + state, + // we need to search across these fields + if (filters.addressTitle && filters.addressTitle.value) { + const searchTerm = filters.addressTitle.value; + // Search in address fields - this is a simplified approach + // In a real implementation, you'd want to join with Customer table and search across all fields addressFilters.address_line1 = ["like", `%${searchTerm}%`]; } - // Add any custom filters + // Add any other custom filters Object.keys(filters).forEach((key) => { - if (filters[key] && filters[key].value) { - // Map frontend filter names to backend field names if needed - switch (key) { - case "fullName": - // This will need special handling since fullName is constructed - // For now, we'll search in address_line1 or city - addressFilters.address_line1 = ["like", `%${filters[key].value}%`]; - break; + if (filters[key] && filters[key].value && key !== "fullName") { + // Map other frontend filter names to backend field names if needed + switch ( + key // Add other filter mappings as needed + ) { } } }); @@ -161,20 +150,15 @@ class Api { }); } - // Apply client-side filtering for constructed fields like fullName + // Since we're applying filters at the database level, use the fetched data as-is let filteredData = data; - if (filters.fullName && filters.fullName.value && forTable) { - const searchValue = filters.fullName.value.toLowerCase(); - filteredData = data.filter((item) => - item.fullName.toLowerCase().includes(searchValue), - ); - } console.log("DEBUG: API - Fetched Client Details:", { total: totalCount, page: page, pageSize: pageSize, returned: filteredData.length, + filtersApplied: Object.keys(addressFilters).length > 0, }); // Return paginated response with metadata @@ -198,7 +182,7 @@ class Api { // ...job, // stepProgress: DataUtils.calculateStepProgress(job.steps), //})); - const projects = await this.getDocsList("Project") + const projects = await this.getDocsList("Project"); const data = []; for (let prj of projects) { let project = await this.getDetailedDoc("Project", prj.name); @@ -207,7 +191,7 @@ class Api { customInstallationAddress: project.custom_installation_address, customer: project.customer, status: project.status, - percentComplete: project.percent_complete + percentComplete: project.percent_complete, }; data.push(tableRow); } @@ -249,8 +233,7 @@ class Api { const { page = 0, pageSize = 10, sortField = null, sortOrder = null } = paginationParams; const options = { - forTable: true, - page, + page: page + 1, pageSize, filters, sortField, @@ -260,8 +243,7 @@ class Api { const result = await this.getClientDetails(options); return { - data: result.data, - totalRecords: result.pagination.total, + ...result, }; } @@ -277,10 +259,13 @@ class Api { const docs = await frappe.db.get_list(doctype, { fields, filters, - start: page * pageLength, + start: start, limit: pageLength, }); - console.log(`DEBUG: API - Fetched ${doctype} list: `, docs); + console.log( + `DEBUG: API - Fetched ${doctype} list (page ${page + 1}, start ${start}): `, + docs, + ); return docs; } diff --git a/frontend/src/components/SideBar.vue b/frontend/src/components/SideBar.vue index 0a28cd7..e90fe01 100644 --- a/frontend/src/components/SideBar.vue +++ b/frontend/src/components/SideBar.vue @@ -34,7 +34,7 @@ const createButtons = ref([ { label: "Client", command: () => { - modalStore.openCreateClient(); + modalStore.openModal("createClient"); }, }, { diff --git a/frontend/src/components/common/DataTable.vue b/frontend/src/components/common/DataTable.vue index 34ff451..e0ad7d5 100644 --- a/frontend/src/components/common/DataTable.vue +++ b/frontend/src/components/common/DataTable.vue @@ -1,6 +1,6 @@