diff --git a/custom_ui/api/db/estimates.py b/custom_ui/api/db/estimates.py index 0368422..64af73a 100644 --- a/custom_ui/api/db/estimates.py +++ b/custom_ui/api/db/estimates.py @@ -65,7 +65,38 @@ def get_estimate(estimate_name): """Get detailed information for a specific estimate.""" try: estimate = frappe.get_doc("Quotation", estimate_name) - return build_success_response(estimate.as_dict()) + est_dict = estimate.as_dict() + + address_name = estimate.custom_installation_address or estimate.customer_address + if address_name: + # Fetch Address Doc + address_doc = frappe.get_doc("Address", address_name).as_dict() + est_dict["full_address"] = address_doc.get("full_address") + + # Logic from get_address_by_full_address to populate customer and contacts + customer_exists = frappe.db.exists("Customer", address_doc.get("custom_customer_to_bill")) + doctype = "Customer" if customer_exists else "Lead" + name = "" + if doctype == "Customer": + name = address_doc.get("custom_customer_to_bill") + else: + lead_links = address_doc.get("links", []) + lead_name = [link.link_name for link in lead_links if link.link_doctype == "Lead"] + name = lead_name[0] if lead_name else "" + + if name: + address_doc["customer"] = frappe.get_doc(doctype, name).as_dict() + + contacts = [] + if address_doc.get("custom_linked_contacts"): + for contact_link in address_doc.get("custom_linked_contacts"): + contact_doc = frappe.get_doc("Contact", contact_link.contact) + contacts.append(contact_doc.as_dict()) + address_doc["contacts"] = contacts + + est_dict["address_details"] = address_doc + + return build_success_response(est_dict) except Exception as e: return build_error_response(str(e), 500) @@ -202,7 +233,7 @@ def update_response(name, response): estimate.custom_response = response estimate.custom_current_status = new_status estimate.custom_followup_needed = 1 if response == "Requested call" else 0 - estimate.status = "Ordered" if accepted else "Closed" + # estimate.status = "Ordered" if accepted else "Closed" estimate.flags.ignore_permissions = True print("DEBUG: Updating estimate with response:", response, "and status:", new_status) estimate.save() @@ -222,7 +253,7 @@ def update_response(name, response): return Response(html, mimetype="text/html") except Exception as e: template = "custom_ui/templates/estimates/error.html" - html = frappe.render_template(template, {"error_message": str(e)}) + html = frappe.render_template(template, {"error": str(e)}) return Response(html, mimetype="text/html") @@ -236,6 +267,7 @@ def upsert_estimate(data): print("DEBUG: Upsert estimate data:", data) estimate_name = data.get("estimate_name") + is_customer = True if frappe.db.exists("Customer", data.get("customer")) else False # If estimate_name exists, update existing estimate if estimate_name: @@ -244,7 +276,8 @@ def upsert_estimate(data): # Update fields estimate.custom_installation_address = data.get("address_name") - estimate.party_name = data.get("contact_name") + estimate.party_name = data.get("customer") + estimate.contact_person = data.get("contact_name") estimate.custom_requires_half_payment = data.get("requires_half_payment", 0) # Clear existing items and add new ones @@ -254,6 +287,8 @@ def upsert_estimate(data): estimate.append("items", { "item_code": item.get("item_code"), "qty": item.get("qty"), + "discount_amount": item.get("discount_amount") or item.get("discountAmount", 0), + "discount_percentage": item.get("discount_percentage") or item.get("discountPercentage", 0) }) estimate.save() @@ -267,18 +302,24 @@ def upsert_estimate(data): new_estimate = frappe.get_doc({ "doctype": "Quotation", "custom_requires_half_payment": data.get("requires_half_payment", 0), - "custom_installation_address": data.get("address_name"), + # "custom_installation_address": data.get("address_name"), "custom_current_status": "Draft", "contact_email": data.get("contact_email"), - "party_name": data.get("contact_name"), - "company": "Sprinklers Northwest", - "customer_name": data.get("customer_name"), + "party_name": data.get("customer"), + "quotation_to": "Customer" if is_customer else "Lead", + "company": data.get("company"), + "customer_name": data.get("customer"), + "customer_address": data.get("address_name"), + "contact_person": data.get("contact_name"), + "letter_head": "company", }) for item in data.get("items", []): item = json.loads(item) if isinstance(item, str) else item new_estimate.append("items", { "item_code": item.get("item_code"), "qty": item.get("qty"), + "discount_amount": item.get("discount_amount") or item.get("discountAmount", 0), + "discount_percentage": item.get("discount_percentage") or item.get("discountPercentage", 0) }) new_estimate.insert() print("DEBUG: New estimate created with name:", new_estimate.name) @@ -286,4 +327,13 @@ def upsert_estimate(data): except Exception as e: print(f"DEBUG: Error in upsert_estimate: {str(e)}") return build_error_response(str(e), 500) - + +# @frappe.whitelist() +# def get_estimate_counts(): +# """Get specific counts of estimates based on their status.""" +# try: +# counts = { +# "total_estimates": frappe.db.count("Quotation"), +# "ready_to_" +# } + \ No newline at end of file diff --git a/custom_ui/commands.py b/custom_ui/commands.py index 16218db..8728158 100644 --- a/custom_ui/commands.py +++ b/custom_ui/commands.py @@ -3,6 +3,51 @@ import os import subprocess import frappe from custom_ui.utils import create_module +from custom_ui.db_utils import search_any_field + +@click.command("update-data") +@click.option("--site", default=None, help="Site to update data for") +def update_data(site): + address_names = frappe.get_all("Address", pluck="name") + total_addresses = len(address_names) + updated_addresses = 0 + updated_contacts = 0 + updated_customers = 0 + total_updated_fields = 0 + skipped = 0 + for address_name in address_names: + should_update = False + address = frappe.get_doc("Address", address_name) + customer_name = address.custom_customer_to_bill + customer_links = [link for link in address.get("links", []) if link.link_doctype == "Customer"] + # lead_links = [link for link in address.get("links", []) if link.link_doctype == "Lead"] + contact_links = [link for link in address.get("links", []) if link.link_doctype == "Contact"] + address.get("custom_linked_contacts", []) + + if frappe.db.exists("Customer", customer_name): + customer = frappe.get_doc("Customer", customer_name) + else: + lead_names = frappe.get_all("Lead", filters={"lead_name": customer_name}, pluck="name") + customer_name = lead_names[0] if lead_names else None + customer = frappe.get_doc("Lead", customer_name) if customer_name else None + if not customer_links and customer and customer.doctype == "Customer": + address.append("links", { + "link_doctype": customer.doctype, + "link_name": customer.name + }) + updated_addresses += 1 + should_update = True + elif not lead_links and customer and customer.doctype == "Lead": + address.append("links", { + "link_doctype": customer.doctype, + "link_name": customer.name + }) + updated_addresses += 1 + should_update = True + + + + + @click.command("build-frontend") @click.option("--site", default=None, help="Site to build frontend for") diff --git a/custom_ui/db_utils.py b/custom_ui/db_utils.py index 2b32c67..3b272cf 100644 --- a/custom_ui/db_utils.py +++ b/custom_ui/db_utils.py @@ -1,4 +1,5 @@ +import frappe import json def map_field_name(frontend_field): @@ -194,4 +195,31 @@ def map_lead_update(client_data): print(f"DEBUG: Mapping field {client_field} to {lead_field} with value {client_data[client_field]}") client_data[lead_field] = client_data[client_field] return client_data - \ No newline at end of file + + +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 + ) \ No newline at end of file diff --git a/custom_ui/events/estimate.py b/custom_ui/events/estimate.py index 7123247..a78fae0 100644 --- a/custom_ui/events/estimate.py +++ b/custom_ui/events/estimate.py @@ -33,7 +33,13 @@ def on_update_after_submit(doc, method): try: new_sales_order = make_sales_order(doc.name) new_sales_order.custom_requires_half_payment = doc.requires_half_payment + new_sales_order.payment_schedule = [] + print("DEBUG: Setting payment schedule for Sales Order") + new_sales_order.set_payment_schedule() + print("DEBUG: Inserting Sales Order:", new_sales_order.as_dict()) + new_sales_order.delivery_date = new_sales_order.transaction_date new_sales_order.insert() + print("DEBUG: Submitting Sales Order") new_sales_order.submit() print("DEBUG: Sales Order created successfully:", new_sales_order.name) except Exception as e: diff --git a/custom_ui/fixtures/custom_field.json b/custom_ui/fixtures/custom_field.json new file mode 100644 index 0000000..da39f75 --- /dev/null +++ b/custom_ui/fixtures/custom_field.json @@ -0,0 +1,59 @@ +[ + { + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Quotation", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "custom_quotation_template", + "fieldtype": "Link", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "amended_from", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Quotation Template", + "length": 0, + "link_filters": null, + "mandatory_depends_on": null, + "modified": "2025-12-23 02:28:02.771813", + "module": null, + "name": "Quotation-custom_quotation_template", + "no_copy": 0, + "non_negative": 0, + "options": "Quotation Template", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "show_dashboard": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + } +] \ No newline at end of file diff --git a/custom_ui/fixtures/doctype.json b/custom_ui/fixtures/doctype.json new file mode 100644 index 0000000..72f23a9 --- /dev/null +++ b/custom_ui/fixtures/doctype.json @@ -0,0 +1,974 @@ +[ + { + "_assign": null, + "_comments": null, + "_last_update": null, + "_liked_by": null, + "_user_tags": null, + "actions": [], + "allow_auto_repeat": 0, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "app": null, + "autoname": null, + "beta": 0, + "color": null, + "colour": null, + "custom": 1, + "default_email_template": null, + "default_print_format": null, + "default_view": null, + "description": null, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "documentation": null, + "editable_grid": 0, + "email_append_to": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Human readable name", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "template_name", + "fieldtype": "Data", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Template Name", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 1, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": "1", + "depends_on": null, + "description": "Hide old templates", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "is_active", + "fieldtype": "Check", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Active", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Optional", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Description", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Audit trail", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "source_quotation", + "fieldtype": "Link", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Created From Quotation", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": "Quotation", + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": "30", + "depends_on": null, + "description": "Quote valid for", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "validity_days", + "fieldtype": "Int", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Default Validity Days", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "items", + "fieldtype": "Table", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Items", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": "Quotation Template Item", + "parent": "Quotation Template", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + } + ], + "force_re_route_to_default_view": 0, + "grid_page_length": 50, + "has_web_view": 0, + "hide_toolbar": 0, + "icon": null, + "image_field": null, + "in_create": 0, + "index_web_pages_for_search": 1, + "is_calendar_and_gantt": 0, + "is_published_field": null, + "is_submittable": 0, + "is_tree": 0, + "is_virtual": 0, + "issingle": 0, + "istable": 0, + "links": [], + "make_attachments_public": 0, + "max_attachments": 0, + "menu_index": null, + "migration_hash": null, + "modified": "2025-12-23 02:03:44.840865", + "module": "Selling", + "name": "Quotation Template", + "naming_rule": "", + "nsm_parent_field": null, + "parent_node": null, + "permissions": [ + { + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "match": null, + "parent": "Quotation Template", + "parentfield": "permissions", + "parenttype": "DocType", + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "select": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "print_outline": null, + "protect_attached_files": 0, + "queue_in_background": 0, + "quick_entry": 0, + "read_only": 0, + "recipient_account_field": null, + "restrict_to_domain": null, + "route": null, + "row_format": "Dynamic", + "rows_threshold_for_grid_search": 20, + "search_fields": null, + "sender_field": null, + "sender_name_field": null, + "show_name_in_global_search": 0, + "show_preview_popup": 0, + "show_title_field_in_link": 0, + "smallicon": null, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "subject": null, + "subject_field": null, + "tag_fields": null, + "timeline_field": null, + "title_field": null, + "track_changes": 0, + "track_seen": 0, + "track_views": 0, + "translated_doctype": 0, + "website_search_field": null + }, + { + "_assign": null, + "_comments": null, + "_last_update": null, + "_liked_by": null, + "_user_tags": null, + "actions": [], + "allow_auto_repeat": 0, + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 1, + "app": null, + "autoname": null, + "beta": 0, + "color": null, + "colour": null, + "custom": 1, + "default_email_template": null, + "default_print_format": null, + "default_view": null, + "description": null, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "documentation": null, + "editable_grid": 1, + "email_append_to": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": "Item code link", + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "item_code", + "fieldtype": "Link", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Item Code", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": "Item", + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "item_name", + "fieldtype": "Data", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Item Name", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Description", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": "1.00", + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "quantity", + "fieldtype": "Float", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Quantity", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "discount_percentage", + "fieldtype": "Float", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Discount %", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "rate", + "fieldtype": "Currency", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "is_virtual": 0, + "label": "Rate", + "length": 0, + "link_filters": null, + "make_attachment_public": 0, + "mandatory_depends_on": null, + "max_height": null, + "no_copy": 0, + "non_negative": 0, + "oldfieldname": null, + "oldfieldtype": null, + "options": null, + "parent": "Quotation Template Item", + "parentfield": "fields", + "parenttype": "DocType", + "permlevel": 0, + "placeholder": null, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "show_dashboard": 0, + "show_on_timeline": 0, + "show_preview_popup": 0, + "sort_options": 0, + "translatable": 0, + "trigger": null, + "unique": 0, + "width": null + } + ], + "force_re_route_to_default_view": 0, + "grid_page_length": 50, + "has_web_view": 0, + "hide_toolbar": 0, + "icon": null, + "image_field": null, + "in_create": 0, + "index_web_pages_for_search": 1, + "is_calendar_and_gantt": 0, + "is_published_field": null, + "is_submittable": 0, + "is_tree": 0, + "is_virtual": 0, + "issingle": 0, + "istable": 1, + "links": [], + "make_attachments_public": 0, + "max_attachments": 0, + "menu_index": null, + "migration_hash": null, + "modified": "2025-12-23 02:00:30.908719", + "module": "Selling", + "name": "Quotation Template Item", + "naming_rule": "", + "nsm_parent_field": null, + "parent_node": null, + "permissions": [], + "print_outline": null, + "protect_attached_files": 0, + "queue_in_background": 0, + "quick_entry": 0, + "read_only": 0, + "recipient_account_field": null, + "restrict_to_domain": null, + "route": null, + "row_format": "Dynamic", + "rows_threshold_for_grid_search": 20, + "search_fields": null, + "sender_field": null, + "sender_name_field": null, + "show_name_in_global_search": 0, + "show_preview_popup": 0, + "show_title_field_in_link": 0, + "smallicon": null, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "subject": null, + "subject_field": null, + "tag_fields": null, + "timeline_field": null, + "title_field": null, + "track_changes": 0, + "track_seen": 0, + "track_views": 0, + "translated_doctype": 0, + "website_search_field": null + } +] \ No newline at end of file diff --git a/custom_ui/hooks.py b/custom_ui/hooks.py index b2c2414..a3b62e2 100644 --- a/custom_ui/hooks.py +++ b/custom_ui/hooks.py @@ -177,6 +177,25 @@ doc_events = { } } +fixtures = [ + { + "dt": "DocType", + "filters": [ + ["name", "in", [ + "Quotation Template", + "Quotation Template Item" + ]] + ] + }, + { + "dt": "Custom Field", + "filters": [ + ["dt", "=", "Quotation"], + ["fieldname", "=", "custom_quotation_template"] + ] + } +] + # Scheduled Tasks # --------------- diff --git a/custom_ui/templates/estimates/error.html b/custom_ui/templates/estimates/error.html index c264dc9..6276ae3 100644 --- a/custom_ui/templates/estimates/error.html +++ b/custom_ui/templates/estimates/error.html @@ -58,6 +58,15 @@ margin-bottom: 15px; line-height: 1.6; } + .error-details { + color: #d32f2f; + font-size: 1em; + margin-top: 20px; + padding: 10px; + background-color: #ffebee; + border-left: 4px solid #d32f2f; + border-radius: 4px; + } .highlight { color: #2196f3; font-weight: 600; @@ -69,6 +78,7 @@
⚠️

Oops! Something went wrong.

We're sorry, but an error occurred. Please try again later or contact support if the problem persists.

+

Error: {{ error or "An unknown error occurred." }}

\ No newline at end of file diff --git a/frontend/src/api.js b/frontend/src/api.js index 8febd1f..cf4b08b 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -269,8 +269,8 @@ class Api { } static async getJobTaskList(jobName) { - if (frappe.db.exists("Project", jobName) { - const result = await request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { data: jobName ) + if (frappe.db.exists("Project", jobName)) { + const result = await request(FRAPPE_GET_JOB_TASK_LIST_METHOD, { data: jobName }) console.log(`DEBUG: API - retrieved task list from job ${jobName}:`, result); return result } diff --git a/frontend/src/components/pages/Estimate.vue b/frontend/src/components/pages/Estimate.vue index 9048950..b375326 100644 --- a/frontend/src/components/pages/Estimate.vue +++ b/frontend/src/components/pages/Estimate.vue @@ -77,9 +77,53 @@ showButtons buttonLayout="horizontal" @input="updateTotal" + class="qty-input" /> Price: ${{ (item.standardRate || 0).toFixed(2) }} - Total: ${{ ((item.qty || 0) * (item.standardRate || 0)).toFixed(2) }} +
+
+ + +
+
+
+
+ Total: ${{ ((item.qty || 0) * (item.standardRate || 0) - (item.discountType === 'percentage' ? ((item.qty || 0) * (item.standardRate || 0) * ((item.discountPercentage || 0) / 100)) : (item.discountAmount || 0))).toFixed(2) }}