From 992672b51bbad410777867fa8ad3fc87574592e4 Mon Sep 17 00:00:00 2001 From: Casey Date: Tue, 13 Jan 2026 08:11:58 -0600 Subject: [PATCH] big update --- custom_ui/api/db/bid_meetings.py | 21 +- custom_ui/api/db/clients.py | 74 ++-- custom_ui/api/db/estimates.py | 13 +- custom_ui/api/db/jobs.py | 8 +- custom_ui/api/db/tasks.py | 1 + custom_ui/events/task.py | 7 + custom_ui/fixtures/doctype.json | 470 +++++++++++++++++++++++++- custom_ui/hooks.py | 8 +- custom_ui/install.py | 105 +++++- custom_ui/services/address_service.py | 2 - frontend/src/api.js | 2 +- 11 files changed, 647 insertions(+), 64 deletions(-) create mode 100644 custom_ui/events/task.py diff --git a/custom_ui/api/db/bid_meetings.py b/custom_ui/api/db/bid_meetings.py index 649e596..7d15895 100644 --- a/custom_ui/api/db/bid_meetings.py +++ b/custom_ui/api/db/bid_meetings.py @@ -1,6 +1,7 @@ import frappe import json from custom_ui.db_utils import build_error_response, build_success_response, process_filters, process_sorting +from custom_ui.services import DbService @frappe.whitelist() def get_week_bid_meetings(week_start, week_end): @@ -66,10 +67,10 @@ def get_unscheduled_bid_meetings(): @frappe.whitelist() -def create_bid_meeting(address, notes=""): +def create_bid_meeting(address, notes="", company=None, contact=None): """Create a new On-Site Meeting with Unscheduled status.""" try: - print(f"DEBUG: Creating meeting with address='{address}', notes='{notes}'") + print(f"DEBUG: Creating meeting with address='{address}', notes='{notes}', company='{company}'") # Validate address parameter if not address or address == "None" or not address.strip(): @@ -77,18 +78,24 @@ def create_bid_meeting(address, notes=""): # Get the address document name from the full address string address_name = frappe.db.get_value("Address", filters={"full_address": address}, fieldname="name") + print(f"DEBUG: Address lookup result: address_name='{address_name}'") - + if not address_name: return build_error_response(f"Address '{address}' not found in the system.", 404) + address_doc = DbService.get("Address", address_name) # Create the meeting with Unscheduled status meeting = frappe.get_doc({ "doctype": "On-Site Meeting", - "address": address_name, + "address": address_doc.name, "notes": notes or "", - "status": "Unscheduled" + "status": "Unscheduled", + "company": company, + "contact": contact, + "party_type": address_doc.customer_type, + "party_name": address_doc.customer_name }) meeting.flags.ignore_permissions = True meeting.insert(ignore_permissions=True) @@ -113,7 +120,9 @@ def update_bid_meeting(name, data): "end_time": None, "notes": None, "assigned_employee": None, - "completed_by": None + "completed_by": None, + "contact": None, + "status": None } try: if isinstance(data, str): diff --git a/custom_ui/api/db/clients.py b/custom_ui/api/db/clients.py index a88f742..3d60665 100644 --- a/custom_ui/api/db/clients.py +++ b/custom_ui/api/db/clients.py @@ -258,6 +258,9 @@ def upsert_client(data): contacts = data.get("contacts", []) # Check for existing address + client_doc = check_and_get_client_doc(customer_name) + if client_doc: + return build_error_response(f"Client with name '{customer_name}' already exists.", 400) if address_exists( data.get("address_line1"), data.get("address_line2"), @@ -268,23 +271,22 @@ def upsert_client(data): return build_error_response("This address already exists. Please use a different address or search for the address to find the associated client.", 400) # Handle customer creation/update - client_doc = check_and_get_client_doc(customer_name) - if not client_doc: - print("#####DEBUG: Creating new lead.") - customer_type = data.get("customer_type", "Individual") - primary_contact = find_primary_contact_or_throw(contacts) - lead_data = { - "first_name": primary_contact.get("first_name"), - "last_name": primary_contact.get("last_name"), - "email_id": primary_contact.get("email"), - "phone": primary_contact.get("phone_number"), - "company": data.get("company"), - "custom_customer_name": customer_name, - "customer_type": customer_type - } - if customer_type == "Company": - lead_data["company_name"] = data.get("customer_name") - client_doc = create_lead(lead_data) + + print("#####DEBUG: Creating new lead.") + customer_type = data.get("customer_type", "Individual") + primary_contact = find_primary_contact_or_throw(contacts) + lead_data = { + "first_name": primary_contact.get("first_name"), + "last_name": primary_contact.get("last_name"), + "email_id": primary_contact.get("email"), + "phone": primary_contact.get("phone_number"), + "company": data.get("company"), + "custom_customer_name": customer_name, + "customer_type": customer_type + } + if customer_type == "Company": + lead_data["company_name"] = data.get("customer_name") + client_doc = create_lead(lead_data) print(f"#####DEBUG: {client_doc.doctype}:", client_doc.as_dict()) # Handle address creation @@ -295,7 +297,9 @@ def upsert_client(data): "city": data.get("city"), "state": data.get("state"), "country": "United States", - "pincode": data.get("pincode") + "pincode": data.get("pincode"), + "customer_type": "Lead", + "customer_name": client_doc.name }) #Handle contact creation @@ -330,24 +334,24 @@ def upsert_client(data): }) contact_docs.append(contact_doc) - ##### Create links - # Customer -> Address - if client_doc.doctype == "Customer": - print("#####DEBUG: Linking address to customer.") - client_doc.append("custom_select_address", { - "address_name": address_doc.name, - }) + # ##### Create links + # # Customer -> Address + # if client_doc.doctype == "Customer": + # print("#####DEBUG: Linking address to customer.") + # client_doc.append("custom_select_address", { + # "address_name": address_doc.name, + # }) - # Customer -> Contact - print("#####DEBUG: Linking contacts to customer.") - for contact_doc in contact_docs: - client_doc.append("custom_add_contacts", { - "contact": contact_doc.name, - "email": contact_doc.custom_email, - "phone": contact_doc.phone, - "role": contact_doc.role - }) - client_doc.save(ignore_permissions=True) + # # Customer -> Contact + # print("#####DEBUG: Linking contacts to customer.") + # for contact_doc in contact_docs: + # client_doc.append("custom_add_contacts", { + # "contact": contact_doc.name, + # "email": contact_doc.custom_email, + # "phone": contact_doc.phone, + # "role": contact_doc.role + # }) + # client_doc.save(ignore_permissions=True) # Address -> Customer/Lead create_address_links(address_doc, client_doc, contact_docs) diff --git a/custom_ui/api/db/estimates.py b/custom_ui/api/db/estimates.py index e743bd5..27bd1a3 100644 --- a/custom_ui/api/db/estimates.py +++ b/custom_ui/api/db/estimates.py @@ -111,14 +111,14 @@ def get_estimate_items(): @frappe.whitelist() def get_estimate_from_address(full_address): address_name = frappe.db.get_value("Address", {"full_address": full_address}, "name") - quotation_name = frappe.db.get_value("Quotation", {"custom_installation_address": address_name}, "name") + quotation_name = frappe.db.get_value("Quotation", {"custom_job_address": address_name}, "name") quotation_doc = frappe.get_doc("Quotation", quotation_name) return build_success_response(quotation_doc.as_dict()) # quotation = frappe.db.sql(""" - # SELECT q.name, q.custom_installation_address + # SELECT q.name, q.custom_job_address # FROM `tabQuotation` q # JOIN `tabAddress` a - # ON q.custom_installation_address = a.name + # ON q.custom_job_address = a.name # WHERE a.full_address =%s # """, (full_address,), as_dict=True) # if quotation: @@ -156,8 +156,8 @@ def send_estimate_email(estimate_name): primary = next((e for e in party.email_ids if e.is_primary), None) email = primary.email_id if primary else party.email_ids[0].email_id - if not email and quotation.custom_installation_address: - address = frappe.get_doc("Address", quotation.custom_installation_address) + if not email and quotation.custom_job_address: + address = frappe.get_doc("Address", quotation.custom_job_address) email = getattr(address, 'email_id', None) if not email: return build_error_response("No email found for the customer or address.", 400) @@ -431,7 +431,8 @@ def upsert_estimate(data): "customer_address": data.get("address_name"), "contact_person": data.get("contact_name"), "letter_head": data.get("company"), - "custom_project_template": data.get("project_template", None) + "custom_project_template": data.get("project_template", None), + "from_onsite_meeting": data.get("onsite_meeting", None) }) for item in data.get("items", []): item = json.loads(item) if isinstance(item, str) else item diff --git a/custom_ui/api/db/jobs.py b/custom_ui/api/db/jobs.py index ac6d250..3f78a3d 100644 --- a/custom_ui/api/db/jobs.py +++ b/custom_ui/api/db/jobs.py @@ -25,11 +25,13 @@ def create_job_from_sales_order(sales_order_name): project_template = frappe.get_doc("Project Template", "SNW Install") new_job = frappe.get_doc({ "doctype": "Project", - "custom_installation_address": sales_order.custom_installation_address, - "project_name": sales_order.custom_installation_address, + "custom_address": sales_order.custom_job_address, + # "custom_installation_address": sales_order.custom_installation_address, + "project_name": sales_order.custom_job_address, "project_template": project_template, "custom_warranty_duration_days": 90, - "sales_order": sales_order + "sales_order": sales_order, + "custom_company": sales_order.company }) new_job.insert() return build_success_response(new_job.as_dict()) diff --git a/custom_ui/api/db/tasks.py b/custom_ui/api/db/tasks.py index 5596464..220aba6 100644 --- a/custom_ui/api/db/tasks.py +++ b/custom_ui/api/db/tasks.py @@ -29,6 +29,7 @@ def get_job_task_table_data(filters={}, sortings={}, page=1, page_size=10): tableRows = [] for task in tasks: + print("DEBUG: Processing task:", task) tableRow = {} tableRow["id"] = task["name"] tableRow["subject"] = task["subject"] diff --git a/custom_ui/events/task.py b/custom_ui/events/task.py new file mode 100644 index 0000000..2e32836 --- /dev/null +++ b/custom_ui/events/task.py @@ -0,0 +1,7 @@ +import frappe + +def before_insert(doc, method): + """Set values before inserting a Task.""" + project_doc = frappe.get_doc("Project", doc.project) + if project_doc.custom_installation_address: + doc.custom_property = project_doc.custom_installation_address \ No newline at end of file diff --git a/custom_ui/fixtures/doctype.json b/custom_ui/fixtures/doctype.json index ca1ae52..9cf4ffc 100644 --- a/custom_ui/fixtures/doctype.json +++ b/custom_ui/fixtures/doctype.json @@ -562,8 +562,8 @@ "make_attachments_public": 0, "max_attachments": 0, "menu_index": null, - "migration_hash": "d5c5525a76e0cba00a479f83d71ac74c", - "modified": "2026-01-09 09:08:21.281081", + "migration_hash": "1db94a5f84dbbad8602c84d9153a9ce5", + "modified": "2026-01-13 03:06:43.486249", "module": "Selling", "name": "Quotation Template", "naming_rule": "", @@ -1060,8 +1060,8 @@ "make_attachments_public": 0, "max_attachments": 0, "menu_index": null, - "migration_hash": "d5c5525a76e0cba00a479f83d71ac74c", - "modified": "2026-01-09 08:52:19.818789", + "migration_hash": "1db94a5f84dbbad8602c84d9153a9ce5", + "modified": "2026-01-13 03:06:43.578733", "module": "Selling", "name": "Quotation Template Item", "naming_rule": "", @@ -1098,5 +1098,467 @@ "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": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "section_break_flvp", + "fieldtype": "Section Break", + "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": null, + "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": "Customer Company Link", + "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": "1db94a5f84dbbad8602c84d9153a9ce5", + "modified": "2026-01-13 03:06:43.634983", + "module": "Custom UI", + "name": "Customer Company Link", + "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 + }, + { + "_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": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "address", + "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": "Address", + "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": "Address", + "parent": "Customer Address Link", + "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": "2026-01-13 04:04:51.277600", + "module": "Custom UI", + "name": "Customer Address Link", + "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 + }, + { + "_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": null, + "documentation_url": null, + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "contact", + "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": "Contact", + "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": "Contact", + "parent": "Customer Contact Link", + "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": "2026-01-13 04:06:36.339054", + "module": "Custom UI", + "name": "Customer Contact Link", + "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 c235838..cab76e2 100644 --- a/custom_ui/hooks.py +++ b/custom_ui/hooks.py @@ -174,6 +174,9 @@ doc_events = { }, "Sales Order": { "on_submit": "custom_ui.events.sales_order.on_submit", + }, + "Task": { + "before_insert": "custom_ui.events.task.before_insert" } } @@ -183,7 +186,10 @@ fixtures = [ "filters": [ ["name", "in", [ "Quotation Template", - "Quotation Template Item" + "Quotation Template Item", + "Customer Company Link", + "Customer Address Link", + "Customer Contact Link" ]] ] }, diff --git a/custom_ui/install.py b/custom_ui/install.py index f19b702..314b8e6 100644 --- a/custom_ui/install.py +++ b/custom_ui/install.py @@ -72,6 +72,36 @@ def add_custom_fields(): print("\nšŸ”§ Adding custom fields to doctypes...") custom_fields = { + "Customer": [ + dict( + fieldname="companies", + label="Companies", + fieldtype="Table", + options="Customer Company Link", + insert_after="customer_type" + ), + dict( + fieldname="from_lead", + label="From Lead", + fieldname="Link", + options="Lead", + insert_after="customer_name" + ), + dict( + fieldname="properties", + label="Properties", + fieldtype="Table", + options="Customer Address Link", + insert_after="customer_name" + ), + dict( + fieldname="contacts", + label="Contacts", + fieldtype="Table", + options="Customer Contact Link", + insert_after="properties" + ) + ], "Lead": [ dict( fieldname="customer_type", @@ -139,6 +169,20 @@ def add_custom_fields(): label="Lead Name", fieldtype="Data", insert_after="custom_customer_to_bill" + ), + dict( + fieldname="customer_type", + label="Customer Type", + fieldtype="Select", + options="Customer\nLead", + insert_after="lead_name" + ), + dict( + fieldname="customer_name", + label="Customer Name", + fieldtype="Dynamic Link", + options="customer_type", + insert_after="customer_type" ) ], "Contact": [ @@ -185,6 +229,34 @@ def add_custom_fields(): fieldtype="Link", options="Employee", insert_after="status" + ), + dict( + fieldname="company", + label="Company", + fieldtype="Link", + options="Company", + insert_after="assigned_employee" + ), + dict( + fieldname="party_type", + label="Party Type", + fieldtype="Select", + options="Customer\nLead", + insert_after="company" + ), + dict( + fieldname="party_name", + label="Party Name", + fieldtype="Dynamic Link", + options="party_type", + insert_after="party_type" + ), + dict( + fieldname="contact", + label="Contact", + fieldtype="Link", + options="Contact", + insert_after="party_name" ) ], "Quotation": [ @@ -220,6 +292,13 @@ def add_custom_fields(): insert_after="custom_installation_address", description="The address where the job will be performed.", allow_on_submit=1 + ), + dict( + fieldname="from_onsite_meeting", + label="From On-Site Meeting", + fieldtype="Link", + options="On-Site Meeting", + insert_after="custom_job_address" ) ], "Sales Order": [ @@ -357,10 +436,12 @@ def update_address_fields(): quotations = frappe.get_all("Quotation", pluck="name") addresses = frappe.get_all("Address", pluck="name") sales_orders = frappe.get_all("Sales Order", pluck="name") + tasks = frappe.get_all("Task", pluck="name") total_addresses = len(addresses) total_quotations = len(quotations) total_sales_orders = len(sales_orders) - total_doctypes = total_addresses + total_quotations + total_sales_orders + total_tasks = len(tasks) + total_doctypes = total_addresses + total_quotations + total_sales_orders + total_tasks combined_doctypes = [] for sales_order in sales_orders: combined_doctypes.append({"doctype": "Sales Order", "name": sales_order}) @@ -368,9 +449,10 @@ def update_address_fields(): combined_doctypes.append({"doctype": "Quotation", "name": quotation}) for address in addresses: combined_doctypes.append({"doctype": "Address", "name": address}) - + for task in tasks: + combined_doctypes.append({"doctype": "Task", "name": task}) - print(f"\nšŸ“ Updating field values for {total_addresses} addresses, {total_quotations} quotations, and {total_sales_orders} sales orders...") + print(f"\nšŸ“ Updating field values for {total_addresses} addresses, {total_quotations} quotations, {total_sales_orders} sales orders, and {total_tasks} tasks...") # Field update counters field_counters = { @@ -389,7 +471,8 @@ def update_address_fields(): 'quotations_updated': 0, 'sales_orders_updated': 0, 'customers_updated': 0, - 'contacts_updated': 0 + 'contacts_updated': 0, + 'tasks_updated': 0 } onsite_meta = frappe.get_meta("On-Site Meeting") @@ -537,7 +620,7 @@ def update_address_fields(): updates['custom_payment_received_status'] = payment_received field_counters['custom_payment_received_status'] += 1 field_counters['total_field_updates'] += 1 - + if updates: frappe.db.set_value("Address", doc['name'], updates) field_counters['addresses_updated'] += 1 @@ -553,7 +636,16 @@ def update_address_fields(): address_doc.save(ignore_permissions=True) field_counters['address_linked_to_customer'] += 1 field_counters['total_field_updates'] += 1 - + if doc['doctype'] == "Task": + for task_name in tasks: + property = frappe.get_value("Task", task_name, "custom_property") + project = frappe.get_value("Task", task_name, "project") + project_address = frappe.get_value("Project", project, "custom_installation_address") + alt_project_address = frappe.get_value("Project", project, "custom_address") + if project_address or alt_project_address: + frappe.db.set_value("Task", task_name, "custom_property", project_address if project_address else alt_project_address) + field_counters['tasks_updated'] += 1 + field_counters['total_field_updates'] += 1 @@ -570,6 +662,7 @@ def update_address_fields(): print(f" • Sales Orders updated: {field_counters['sales_orders_updated']:,}") print(f" • Customers updated: {field_counters['customers_updated']:,}") print(f" • Total field updates: {field_counters['total_field_updates']:,}") + print(f" • Tasks Updated: {field_counters['tasks_updated']:,}") print(f"\nšŸ“ Field-specific updates:") print(f" • Full Address: {field_counters['full_address']:,}") print(f" • On-Site Meeting Status: {field_counters['custom_onsite_meeting_scheduled']:,}") diff --git a/custom_ui/services/address_service.py b/custom_ui/services/address_service.py index 4ee3eca..e5d68c8 100644 --- a/custom_ui/services/address_service.py +++ b/custom_ui/services/address_service.py @@ -76,6 +76,4 @@ class AddressService: address.insert(ignore_permissions=True) print("DEBUG: Created new Address:", address.as_dict()) return address - - \ No newline at end of file diff --git a/frontend/src/api.js b/frontend/src/api.js index d7a3798..0a3f447 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -22,7 +22,7 @@ const FRAPPE_GET_JOB_TASK_LIST_METHOD = "custom_ui.api.db.jobs.get_job_task_tabl const FRAPPE_GET_INSTALL_PROJECTS_METHOD = "custom_ui.api.db.jobs.get_install_projects"; const FRAPPE_GET_JOB_TEMPLATES_METHOD = "custom_ui.api.db.jobs.get_job_templates"; // Task methods -const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_tasks_table_data"; +const FRAPPE_GET_TASKS_METHOD = "custom_ui.api.db.tasks.get_job_task_table_data"; // Invoice methods const FRAPPE_GET_INVOICES_METHOD = "custom_ui.api.db.invoices.get_invoice_table_data"; const FRAPPE_UPSERT_INVOICE_METHOD = "custom_ui.api.db.invoices.upsert_invoice";