Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 12 additions & 33 deletions uph/party/doctype/party_master/party_master.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,56 +208,35 @@ 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 :")
);
}
});
}
}

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;
Expand Down
6 changes: 3 additions & 3 deletions uph/party/doctype/party_master/party_master.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
12 changes: 10 additions & 2 deletions uph/party/page/data_quality_dashboard/data_quality_dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
57 changes: 40 additions & 17 deletions uph/party/page/data_quality_dashboard/data_quality_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down