From cb33d0c3b356fddc27e05aa487e0ff2d022dfc15 Mon Sep 17 00:00:00 2001 From: Casey Wittrock Date: Tue, 25 Nov 2025 06:22:44 -0600 Subject: [PATCH] get create client working --- custom_ui/api/db/clients.py | 40 +- custom_ui/api/proxy.py | 5 +- frontend/package-lock.json | 7 + frontend/package.json | 1 + frontend/src/api.js | 16 +- .../clientSubPages/AddressInformationForm.vue | 198 +++++++ .../clientSubPages/ClientInformationForm.vue | 322 ++++++++++ .../clientSubPages/ContactInformationForm.vue | 361 ++++++++++++ .../components/clientSubPages/Overview.vue | 548 +++++++----------- frontend/src/main.js | 1 + frontend/src/style.css | 16 + 11 files changed, 1179 insertions(+), 336 deletions(-) create mode 100644 frontend/src/components/clientSubPages/AddressInformationForm.vue create mode 100644 frontend/src/components/clientSubPages/ClientInformationForm.vue create mode 100644 frontend/src/components/clientSubPages/ContactInformationForm.vue diff --git a/custom_ui/api/db/clients.py b/custom_ui/api/db/clients.py index a9d6baf..4a08a8f 100644 --- a/custom_ui/api/db/clients.py +++ b/custom_ui/api/db/clients.py @@ -96,6 +96,8 @@ def get_client(client_name): customer = frappe.get_doc("Customer", client_name) clientData = {**clientData, **customer.as_dict()} addresses = frappe.db.get_all("Address", fields=["*"], filters={"custom_customer_to_bill": client_name}) + contacts = frappe.db.get_all("Contact", fields=["*"], filters={"custom_customer": client_name}) + clientData["contacts"] = contacts for address in addresses if addresses else []: addressData = {"jobs": []} addressData = {**addressData, **address} @@ -263,7 +265,7 @@ def upsert_client(data): "customer_type": data.get("customer_type") }).insert(ignore_permissions=True) else: - customer_doc = frappe.get_doc("Customer", customer) + customer_doc = frappe.get_doc("Customer", data.get("customer_name")) print("Customer:", customer_doc.as_dict()) @@ -281,6 +283,7 @@ def upsert_client(data): # Create address address_doc = frappe.get_doc({ "doctype": "Address", + "address_title": data.get("address_title"), "address_line1": data.get("address_line1"), "address_line2": data.get("address_line2"), "city": data.get("city"), @@ -297,12 +300,43 @@ def upsert_client(data): } address_doc.append("links", link) address_doc.save(ignore_permissions=True) + contact_exists = frappe.db.exists("Contact", {"email_id": data.get("contact_email")}) + if not contact_exists: + contact_doc = frappe.get_doc({ + "doctype": "Contact", + "first_name": data.get("first_name"), + "last_name": data.get("last_name"), + "email_id": data.get("email"), + "phone": data.get("phone_number"), + "custom_customer": customer_doc.name, + "links": [{ + "link_doctype": "Customer", + "link_name": customer_doc.name + }] + }).insert(ignore_permissions=True) + print("Created new contact:", contact_doc.as_dict()) + else: + contact_doc = frappe.get_doc("Contact", {"email_id": data.get("contact_email")}) + print("Contact already exists:", contact_doc.as_dict()) return build_success_response({ "customer": customer_doc.as_dict(), - "address": address_doc.as_dict() + "address": address_doc.as_dict(), + "contact": contact_doc.as_dict() }) except frappe.ValidationError as ve: return build_error_response(str(ve), 400) except Exception as e: - return build_error_response(str(e), 500) \ No newline at end of file + return build_error_response(str(e), 500) + +@frappe.whitelist() +def get_client_names(search_term): + """Search for client names matching the search term.""" + try: + search_pattern = f"%{search_term}%" + client_names = frappe.db.get_all( + "Customer", + pluck="name") + return build_success_response(client_names) + except Exception as e: + return build_error_response(str(e), 500) diff --git a/custom_ui/api/proxy.py b/custom_ui/api/proxy.py index 7c45473..60967f3 100644 --- a/custom_ui/api/proxy.py +++ b/custom_ui/api/proxy.py @@ -1,6 +1,7 @@ import frappe import requests from urllib.parse import urlparse +from custom_ui.db_utils import build_success_response, build_error_response import logging allowed_hosts = ["api.zippopotam.us", "nominatim.openstreetmap.org"] # Update this list with trusted domains as needed @@ -24,9 +25,9 @@ def request(url, method="GET", data=None, headers=None): ) resp.raise_for_status() try: - return resp.json() + return build_success_response(resp.json()) except ValueError: - return {"text": resp.text} + return build_success_response({"text": resp.text}) except requests.exceptions.RequestException as e: frappe.log_error(message=str(e), title="Proxy Request Failed") frappe.throw("Failed to fetch data from external API.") \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c6c74c2..d36ac0c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16,6 +16,7 @@ "frappe-ui": "^0.1.205", "leaflet": "^1.9.4", "pinia": "^3.0.3", + "primeicons": "^7.0.0", "primevue": "^4.4.1", "vue": "^3.5.22", "vue-chartjs": "^5.3.3", @@ -3634,6 +3635,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, "node_modules/primevue": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/primevue/-/primevue-4.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index ce9bf05..96a7bec 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "frappe-ui": "^0.1.205", "leaflet": "^1.9.4", "pinia": "^3.0.3", + "primeicons": "^7.0.0", "primevue": "^4.4.1", "vue": "^3.5.22", "vue-chartjs": "^5.3.3", diff --git a/frontend/src/api.js b/frontend/src/api.js index 8557562..a273954 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -328,8 +328,12 @@ class Api { return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type }); } - static async getClientNames(type) { - return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { type }); + static async getClientNames(clientName) { + return await this.request(FRAPPE_GET_CLIENT_NAMES_METHOD, { searchTerm: clientName }); + } + + static async searchClientNames(searchTerm) { + return await this.request("custom_ui.api.db.clients.search_client_names", { searchTerm }); } static async getCompanyNames() { @@ -342,8 +346,7 @@ class Api { // Create methods static async createClient(clientData) { - const payload = DataUtils.toSnakeCaseObject(clientData); - const result = await this.request(FRAPPE_UPSERT_CLIENT_METHOD, { data: payload }); + const result = await this.request(FRAPPE_UPSERT_CLIENT_METHOD, { data: clientData }); console.log("DEBUG: API - Created/Updated Client: ", result); return result; } @@ -391,7 +394,10 @@ class Api { if (!places || places.length === 0) { throw new Error(`No location data found for zip code ${zipcode}`); } - return places; + return places.map((place) => ({ + city: place["place name"], + state: place["state abbreviation"], + })); } static async getGeocode(address) { diff --git a/frontend/src/components/clientSubPages/AddressInformationForm.vue b/frontend/src/components/clientSubPages/AddressInformationForm.vue new file mode 100644 index 0000000..77390cd --- /dev/null +++ b/frontend/src/components/clientSubPages/AddressInformationForm.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/frontend/src/components/clientSubPages/ClientInformationForm.vue b/frontend/src/components/clientSubPages/ClientInformationForm.vue new file mode 100644 index 0000000..04132ae --- /dev/null +++ b/frontend/src/components/clientSubPages/ClientInformationForm.vue @@ -0,0 +1,322 @@ + + + + + diff --git a/frontend/src/components/clientSubPages/ContactInformationForm.vue b/frontend/src/components/clientSubPages/ContactInformationForm.vue new file mode 100644 index 0000000..8f4df0c --- /dev/null +++ b/frontend/src/components/clientSubPages/ContactInformationForm.vue @@ -0,0 +1,361 @@ + + + + + diff --git a/frontend/src/components/clientSubPages/Overview.vue b/frontend/src/components/clientSubPages/Overview.vue index 99ca138..6baf61d 100644 --- a/frontend/src/components/clientSubPages/Overview.vue +++ b/frontend/src/components/clientSubPages/Overview.vue @@ -1,172 +1,113 @@