add query for client details

This commit is contained in:
Casey Wittrock 2025-11-13 15:17:43 -06:00
parent 172927e069
commit 0b280cec8e
8 changed files with 114 additions and 69 deletions

View File

@ -92,50 +92,90 @@ def get_client_status_counts(weekly=False, week_start_date=None, week_end_date=N
def get_client(client_name):
"""Get detailed information for a specific client including address, customer, and projects."""
try:
address = frappe.get_doc("Address", client_name)
customer_name = address.custom_customer_to_bill if address.custom_customer_to_bill else [link.link_name for link in address.links if link.link_doctype == "Customer"][0] if address.links else None
if not customer_name:
raise Exception(f"No customer linked to address {client_name}. Suggested fix: Ensure the address is linked to a customer via the ERPnext UI.")
project_names = frappe.db.get_all("Project", fields=["name"], or_filters=[
["custom_installation_address", "=", address.address_title],
["custom_address", "=", address.address_title]
], limit_page_length=100)
# contacts = [] # currently not needed as the customer doctype comes with contacts
onsite_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 = []
projects = [frappe.get_doc("Project", proj["name"]) for proj in project_names]
sales_invoices = []
payment_entries = frappe.db.get_all(
doctype="Payment Entry",
fields=["*"],
filters={"party": customer_name})
payment_orders = []
jobs = []
for project in projects:
job = []
jobs.append(job)
customer = frappe.get_doc("Customer", customer_name)
# get all associated data as needed
return build_success_response({
"address": address,
"customer": customer,
# "contacts": [], # currently not needed as the customer doctype comes with contacts
"jobs": jobs,
"sales_invoices": sales_invoices,
"payment_entries": payment_entries,
"sales_orders": sales_orders,
"quotations": quotations,
"onsite_meetings": onsite_meetings,
})
clientData = {"addresses": []}
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})
for address in addresses if addresses else []:
addressData = {"jobs": []}
addressData = {**addressData, **address}
addressData["estimates"] = frappe.db.get_all("Quotation", fields=["*"], filters={"custom_installation_address": address.address_title})
addressData["onsite_meetings"] = frappe.db.get_all("On-Site Meeting", fields=["*"], filters={"address": address.address_title})
jobs = frappe.db.get_all("Project", fields=["*"], or_filters=[
["custom_installation_address", "=", address.address_title],
["custom_address", "=", address.address_title]
])
for job in jobs if jobs else []:
jobData = {}
jobData = {**jobData, **job}
jobData["sales_invoices"] = frappe.db.get_all("Sales Invoice", fields=["*"], filters={"project": job.name})
jobData["payment_entries"] = frappe.db.get_all(
"Payment Entry",
fields=["*"],
filters={"party_type": "Customer"},
or_filters=[
["party", "=", client_name],
["party_name", "=", client_name]
])
jobData["sales_orders"] = frappe.db.get_all("Sales Order", fields=["*"], filters={"project": job.name})
jobData["tasks"] = frappe.db.get_all("Task", fields=["*"], filters={"project": job.name})
addressData["jobs"].append(jobData)
clientData["addresses"].append(addressData)
return build_success_response(clientData)
except frappe.ValidationError as ve:
return build_error_response(str(ve), 400)
except Exception as e:
return build_error_response(str(e), 500)
# address = frappe.get_doc("Address", client_name)
# customer_name = address.custom_customer_to_bill if address.custom_customer_to_bill else [link.link_name for link in address.links if link.link_doctype == "Customer"][0] if address.links else None
# if not customer_name:
# raise Exception(f"No customer linked to address {client_name}. Suggested fix: Ensure the address is linked to a customer via the ERPnext UI.")
# project_names = frappe.db.get_all("Project", fields=["name"], or_filters=[
# ["custom_installation_address", "=", address.address_title],
# ["custom_address", "=", address.address_title]
# ], limit_page_length=100)
# # contacts = [] # currently not needed as the customer doctype comes with contacts
# onsite_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 = []
# projects = [frappe.get_doc("Project", proj["name"]) for proj in project_names]
# sales_invoices = []
# payment_entries = frappe.db.get_all(
# "Payment Entry",
# fields=["*"],
# filters={"party_type": "Customer"},
# or_filters=[
# ["party", "=", customer_name],
# ["party_name", "=", customer_name]
# ])
# payment_orders = []
# jobs = []
# for project in projects:
# job = []
# jobs.append(job)
# customer = frappe.get_doc("Customer", customer_name)
# # get all associated data as needed
# return build_success_response({
# "address": address,
# "customer": customer,
# # "contacts": [], # currently not needed as the customer doctype comes with contacts
# "jobs": jobs,
# "sales_invoices": sales_invoices,
# "payment_entries": payment_entries,
# "sales_orders": sales_orders,
# "quotations": quotations,
# "onsite_meetings": onsite_meetings,
# })
except frappe.ValidationError as ve:
return build_error_response(str(ve), 400)
except Exception as e:

View File

@ -68,6 +68,7 @@ def process_filters(filters):
def process_sorting(sortings):
sortings = json.loads(sortings) if isinstance(sortings, str) else sortings
order_by = ""
print("DEBUG: Original sorting:", sortings)
if sortings and len(sortings) > 0:
for sorting in sortings:
mapped_field = map_field_name(sorting[0].strip())

View File

@ -2,10 +2,8 @@ import frappe
def after_insert(doc, method):
print(doc.address)
frappe.msgprint(f"On-Site Meeting '{doc.name}' has been created. Updating related records...")
address_name = frappe.db.get_value("Address", fieldname="name", filters={"address_line1": doc.address})
address_doc = frappe.get_doc("Address", address_name)
frappe.msgprint(f"Related Address '{address_doc.address_title}' has been retrieved.")
address_doc.custom_onsite_meeting_scheduled = "Completed"
address_doc.save()
frappe.msgprint(f"Related Address '{address_doc.address_title}' has been updated with custom_onsite_meeting_scheduled = 'Completed'.")
if doc.address:
address_name = frappe.db.get_value("Address", fieldname="name", filters={"address_line1": doc.address})
address_doc = frappe.get_doc("Address", address_name)
address_doc.custom_onsite_meeting_scheduled = "Completed"
address_doc.save()

View File

@ -10,10 +10,9 @@ def after_insert(doc, method):
def after_save(doc, method):
if doc.custome_sent:
address_title = doc.custom_installation_address
address_name = frappe.db.get_value("Address", fieldname="name", filters={"address_title": address_title})
if address_name:
address_doc = frappe.get_doc("Address", address_name)
address_doc.custom_quotation_sent = "Completed"
address_doc.save()
address_title = doc.custom_installation_address
address_name = frappe.db.get_value("Address", fieldname="name", filters={"address_title": address_title})
if doc.custome_sent and address_name:
address_doc = frappe.get_doc("Address", address_name)
address_doc.custom_quotation_sent = "Completed"
address_doc.save()

View File

@ -1,4 +1,5 @@
import ApiUtils from "./apiUtils";
import { useErrorStore } from "./stores/errors";
const ZIPPOPOTAMUS_BASE_URL = "https://api.zippopotam.us/us";
const FRAPPE_PROXY_METHOD = "custom_ui.api.proxy.request";
@ -15,6 +16,7 @@ const FRAPPE_GET_CLIENT_NAMES_METHOD = "custom_ui.api.db.clients.get_client_name
class Api {
static async request(frappeMethod, args = {}) {
const errorStore = useErrorStore();
args = ApiUtils.toSnakeCaseObject(args);
const request = { method: frappeMethod, args };
console.log("DEBUG: API - Request Args: ", request);
@ -28,6 +30,7 @@ class Api {
return response.message.data;
} catch (error) {
console.error("ERROR: API - Request Error: ", error);
errorStore.setApiError("Frappe API", error.message || "API request error");
throw error;
}
}
@ -112,11 +115,11 @@ class Api {
* @param {Object} sorting - Sorting parameters from store (optional)
* @returns {Promise<{data: Array, pagination: Object}>}
*/
static async getPaginatedClientDetails(paginationParams = {}, filters = {}, sorting = []) {
static async getPaginatedClientDetails(paginationParams = {}, filters = {}, sortings = []) {
const { page = 0, pageSize = 10 } = paginationParams;
const result = await this.request(FRAPPE_GET_CLIENT_TABLE_DATA_METHOD, {
filters,
sorting,
sortings,
page: page + 1,
pageSize,
});

View File

@ -3,7 +3,7 @@ class ApiUtils {
console.log("Converting to snake case:", obj);
const newObj = Object.entries(obj).reduce((acc, [key, value]) => {
const snakeKey = key.replace(/[A-Z]/g, (match) => "_" + match.toLowerCase());
if (key === "sorting") {
if (key === "sortings") {
value = value
? value.map((item) => {
const [field, order] = item;

View File

@ -37,10 +37,9 @@ import TabPanel from "primevue/tabpanel";
import Api from "../../api";
import ApiWithToast from "../../api-toast";
import { useLoadingStore } from "../../stores/loading";
import { useErrorStore } from "../../stores/errors";
const loadingStore = useLoadingStore();
const errorStore = useErrorStore();
const clientNames = ref([]);
const client = ref({});
const { clientName } = defineProps({
@ -50,10 +49,10 @@ const { clientName } = defineProps({
const getClientNames = async (type) => {
loadingStore.setLoading(true);
try {
const names = await Api.getCustomerNames(type);
const names = await Api.getClientNames(type);
clientNames.value = names;
} catch (error) {
errorStore.addError(error.message || "Error fetching client names");
console.error("Error fetching client names in Client.vue: ", error.message || error);
} finally {
loadingStore.setLoading(false);
}
@ -61,9 +60,14 @@ const getClientNames = async (type) => {
const getClient = async (name) => {
loadingStore.setLoading(true);
const clientData = await ApiWithToast.makeApiCall(() => Api.getClient(name));
client.value = clientData || {};
loadingStore.setLoading(false);
try {
const clientData = await Api.getClient(name);
client.value = clientData || {};
} catch (error) {
console.error("Error fetching client data in Client.vue: ", error.message || error);
} finally {
loadingStore.setLoading(false);
}
};
onMounted(async () => {

View File

@ -165,7 +165,7 @@ const tableActions = [
{
label: "View Details",
action: (rowData) => {
router.push(`/clients/${rowData.id}`);
router.push(`/clients/${rowData.customerName}`);
},
type: "button",
style: "info",