add history component

This commit is contained in:
Casey 2025-12-30 12:33:29 -06:00
parent b8fea2c9ca
commit 58e69596bb
8 changed files with 404 additions and 58 deletions

View file

@ -130,7 +130,8 @@ def get_client(client_name):
clientData["contacts"].append(linked_doc.as_dict())
elif link["link_doctype"] == "Address":
clientData["addresses"].append(linked_doc.as_dict())
# TODO: Continue getting other linked docs like jobs, invoices, etc.
doctypes_to_fetch_history = []
# TODO: Continue getting other linked docs like jobs, invoices, etc.
print("DEBUG: Final client data prepared:", clientData)
return build_success_response(clientData)
except frappe.ValidationError as ve:

View file

@ -1,5 +1,6 @@
import frappe, json
from frappe.utils.pdf import get_pdf
from custom_ui.api.db.general import get_doc_history
from custom_ui.db_utils import process_query_conditions, build_datatable_dict, get_count_or_filters, build_success_response, build_error_response
from werkzeug.wrappers import Response
from custom_ui.api.db.clients import check_if_customer, convert_lead_to_customer
@ -95,6 +96,8 @@ def get_estimate(estimate_name):
address_doc["contacts"] = contacts
est_dict["address_details"] = address_doc
est_dict["history"] = get_doc_history("Quotation", estimate_name)
return build_success_response(est_dict)
except Exception as e:
@ -293,8 +296,10 @@ def upsert_estimate(data):
})
estimate.save()
estimate_dict = estimate.as_dict()
estimate_dict["history"] = get_doc_history("Quotation", estimate_name)
print(f"DEBUG: Estimate updated: {estimate.name}")
return build_success_response(estimate.as_dict())
return build_success_response(estimate_dict)
# Otherwise, create new estimate
else:
@ -329,6 +334,11 @@ def upsert_estimate(data):
print(f"DEBUG: Error in upsert_estimate: {str(e)}")
return build_error_response(str(e), 500)
def get_estimate_history(estimate_name):
"""Get the history of changes for a specific estimate."""
return history
# @frappe.whitelist()
# def get_estimate_counts():
# """Get specific counts of estimates based on their status."""

View file

@ -0,0 +1,59 @@
import frappe
from custom_ui.db_utils import build_history_entries
def get_doc_history(doctype, docname):
"""Get the history of changes for a specific document."""
# Fetch comments
comments = frappe.get_all(
"Comment",
filters={
"reference_doctype": doctype,
"reference_name": docname
},
fields=["*"],
order_by="creation desc"
)
versions = frappe.get_all(
"Version",
filters={"docname": docname, "ref_doctype": doctype},
fields=["*"],
order_by="creation desc"
)
history_entries = build_history_entries(comments, versions)
print(f"DEBUG: Retrieved history for {doctype} {docname}: {history_entries}")
return history_entries
def get_docs_history(doctypes_with_names):
"""Get history for multiple documents."""
all_history = {}
for doctype, docname in doctypes_with_names:
history = get_doc_history(doctype, docname)
all_history[f"{doctype}:{docname}"] = history
return all_history
def search_any_field(doctype, text):
meta = frappe.get_meta(doctype)
like = f"%{text}%"
conditions = []
# 1⃣ Explicitly include `name`
conditions.append("`name` LIKE %s")
# 2⃣ Include searchable DocFields
for field in meta.fields:
if field.fieldtype in ("Data", "Small Text", "Text", "Link"):
conditions.append(f"`{field.fieldname}` LIKE %s")
query = f"""
SELECT name
FROM `tab{doctype}`
WHERE {" OR ".join(conditions)}
LIMIT 20
"""
return frappe.db.sql(
query,
[like] * len(conditions),
as_dict=True
)

View file

@ -3,7 +3,7 @@ import os
import subprocess
import frappe
from custom_ui.utils import create_module
from custom_ui.db_utils import search_any_field
from custom_ui.api.db.general import search_any_field
@click.command("update-data")
@click.option("--site", default=None, help="Site to update data for")

View file

@ -1,6 +1,5 @@
import frappe
import json
from frappe.utils import strip_html
def map_field_name(frontend_field):
field_mapping = {
@ -196,30 +195,37 @@ def map_lead_update(client_data):
client_data[lead_field] = client_data[client_field]
return client_data
def search_any_field(doctype, text):
meta = frappe.get_meta(doctype)
like = f"%{text}%"
conditions = []
# 1⃣ Explicitly include `name`
conditions.append("`name` LIKE %s")
# 2⃣ Include searchable DocFields
for field in meta.fields:
if field.fieldtype in ("Data", "Small Text", "Text", "Link"):
conditions.append(f"`{field.fieldname}` LIKE %s")
query = f"""
SELECT name
FROM `tab{doctype}`
WHERE {" OR ".join(conditions)}
LIMIT 20
"""
return frappe.db.sql(
query,
[like] * len(conditions),
as_dict=True
)
def map_comment_to_history_entry(comment):
return {
"type": comment.get("comment_type", "Comment"),
"user": comment.get("owner"),
"timestamp": comment.get("creation"),
"message": strip_html(comment.get("content", ""))
}
def map_version_data_to_history_entry(changed_data, creation, owner):
field, old, new = changed_data
return {
"type": "Field Change",
"timestamp": creation,
"user": owner,
"message": f"Changed '{field}' from '{old}' to '{new}'"
}
def build_history_entries(comments, versions):
history = []
for comment in comments:
history.append(map_comment_to_history_entry(comment))
for version in versions:
data = json.loads(version.get("data", "[]"))
for changed_data in data.get("changed", []):
entry = map_version_data_to_history_entry(
changed_data,
version.get("creation"),
version.get("owner")
)
if entry:
history.append(entry)
# Sort by timestamp descending
history.sort(key=lambda x: x["timestamp"], reverse=True)
return history