diff --git a/uph/party/doctype/party_master/party_master.js b/uph/party/doctype/party_master/party_master.js index a473cf98..a0886022 100644 --- a/uph/party/doctype/party_master/party_master.js +++ b/uph/party/doctype/party_master/party_master.js @@ -208,22 +208,28 @@ function update_buttons(frm) { let party_count = counts[frm.doc.party_type] || 0; - // Primary action: Linking existing parties + // Primary action: Route to Data Quality Dashboard if (party_count > 0) { frm.page.set_primary_action( - __("Linking Existing {0} ({1})", [frm.doc.party_type, party_count]), - () => fetch_existing_parties(frm, { party_type: frm.doc.party_type }) + __("Unlinked Roles in Party Issue ({0})", [party_count]), + () => { + frappe.route_options = { party_master: frm.doc.name }; + frappe.set_route("data-quality-dashboard"); + } ); } - // Secondary roles + // Secondary roles: Also route to Data Quality Dashboard if (frm.doc.has_secondary_role_party && frm.doc.roles) { frm.doc.roles.forEach((role) => { const role_count = counts[role.party_type_role] || 0; if (role_count > 0) { frm.add_custom_button( - __("Linking Existing {0} ({1})", [role.party_type_role, role_count]), - () => fetch_existing_parties(frm, { party_type: role.party_type_role }), + __("Unlinked Roles in Party Issue ({0})", [role_count]), + () => { + frappe.route_options = { party_master: frm.doc.name }; + frappe.set_route("data-quality-dashboard"); + }, __("Fetch From :") ); } @@ -231,33 +237,6 @@ function update_buttons(frm) { } } -function fetch_existing_parties(frm, filters) { - const dialog = new frappe.ui.form.MultiSelectDialog({ - doctype: filters.party_type, - target: frm, - setters: {}, - columns: ["name", "party_name", "currency"], - add_filters_group: 1, - get_query() { - return { - query: "uph.party.doctype.party_master.party_master.get_unset_parties_list", - filters: { party_master: frm.doc.name, unset: 1, party_type: filters.party_type }, - }; - }, - action(selections) { - const data = selections.map((name) => ({ name, party_type: filters.party_type })); - frappe.call({ - method: "set_party_master", - doc: frm.doc, - args: { data }, - callback() { - frm.refresh_field("linked_party"); - dialog.dialog.hide(); - }, - }); - }, - }); -} function get_counts_unlinked_parties() { if (frappe.boot.unlinked_parties_counts) return frappe.boot.unlinked_parties_counts; diff --git a/uph/party/doctype/party_master/party_master.json b/uph/party/doctype/party_master/party_master.json index 274b1320..62accaf1 100644 --- a/uph/party/doctype/party_master/party_master.json +++ b/uph/party/doctype/party_master/party_master.json @@ -83,6 +83,7 @@ "column_break_trade_right", "default_cost_center", "default_project", + "default_party_analytic_accounting", "section_break_internal_accounting", "is_internal_party", "represents_company", @@ -95,7 +96,6 @@ "section_break_analytics", "enforce_party_analaytic_accounting_selection", "column_break_analytics_right", - "default_party_analytic_accounting", "tax_tab", "section_break_taxation", "tax_id", @@ -823,8 +823,8 @@ "is_calendar_and_gantt": 1, "is_tree": 1, "links": [], - "modified": "2026-03-06 23:51:31.093068", - "modified_by": "ruzaqi@gmail.com", + "modified": "2026-03-07 00:23:32.445097", + "modified_by": "Administrator", "module": "Party", "name": "Party Master", "naming_rule": "By script", diff --git a/uph/party/page/data_quality_dashboard/data_quality_dashboard.js b/uph/party/page/data_quality_dashboard/data_quality_dashboard.js index bb4e8267..15a7a8b1 100644 --- a/uph/party/page/data_quality_dashboard/data_quality_dashboard.js +++ b/uph/party/page/data_quality_dashboard/data_quality_dashboard.js @@ -38,8 +38,16 @@ class DataQualityDashboard { this.setup_page_actions(); this.render_layout(); this.setup_filters(); - this.load_stats(); - this.load_tab_content(); + + if (frappe.route_options && frappe.route_options.party_master) { + const pm = frappe.route_options.party_master; + frappe.route_options = null; + // Don't force 'unlinked' tab, let it default or be specified + this.party_master_field.set_value(pm); + } else { + this.load_stats(); + this.load_tab_content(); + } } setup_filters() { diff --git a/uph/party/page/data_quality_dashboard/data_quality_dashboard.py b/uph/party/page/data_quality_dashboard/data_quality_dashboard.py index 32d22c08..8b7a9146 100644 --- a/uph/party/page/data_quality_dashboard/data_quality_dashboard.py +++ b/uph/party/page/data_quality_dashboard/data_quality_dashboard.py @@ -26,24 +26,47 @@ def get_duplicate_issues( filters = {"issue_type": "Duplicate", "status": ["in", ["Open", "Under Review"]]} if float(min_score) > 0: filters["score"] = [">=", float(min_score)] - if party_master: - filters["party_master"] = party_master - duplicates = frappe.get_all( - "Party Issue", - fields=[ - "name", - "party_master", - "reference_doctype", - "reference_name", - "score", - "status", - ], - filters=filters, - order_by="score desc", - limit_start=offset, - limit_page_length=limit, - ) + if party_master: + # Filter for issues where the party is either on the left or the right + # We use manual SQL here because complex OR across different fields is tricky in Frappe's filters dict + where_clause = """ + issue_type = 'Duplicate' + AND status IN ('Open', 'Under Review') + AND (party_master = %(pm)s OR (reference_doctype = 'Party Master' AND reference_name = %(pm)s)) + """ + params = {"pm": party_master} + if float(min_score) > 0: + where_clause += " AND score >= %(score)s" + params["score"] = float(min_score) + + duplicates = frappe.db.sql( + f""" + SELECT name, party_master, reference_doctype, reference_name, score, status + FROM `tabParty Issue` + WHERE {where_clause} + ORDER BY score DESC + LIMIT %(limit)s OFFSET %(offset)s + """, + {**params, "limit": limit, "offset": offset}, + as_dict=True, + ) + else: + duplicates = frappe.get_all( + "Party Issue", + fields=[ + "name", + "party_master", + "reference_doctype", + "reference_name", + "score", + "status", + ], + filters=filters, + order_by="score desc", + limit_start=offset, + limit_page_length=limit, + ) # Bulk-fetch party names to avoid N+1 queries if duplicates: