From 039c47e3f294d45083bca3f1ac4e759d15c72390 Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Fri, 6 Jun 2025 18:40:06 +0530 Subject: [PATCH 1/9] fix: patch to set discount percentange in case of mismatch (cherry picked from commit f7eda8a156df74053a0a773c50032db8de2eb67b) --- .../set_additional_discount_percentage.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 erpnext/patches/v15_0/set_additional_discount_percentage.py diff --git a/erpnext/patches/v15_0/set_additional_discount_percentage.py b/erpnext/patches/v15_0/set_additional_discount_percentage.py new file mode 100644 index 00000000000..2015bfd6c32 --- /dev/null +++ b/erpnext/patches/v15_0/set_additional_discount_percentage.py @@ -0,0 +1,56 @@ +import frappe +from frappe import scrub +from frappe.model.meta import get_field_precision +from frappe.utils import flt + +from erpnext.accounts.report.calculated_discount_mismatch.calculated_discount_mismatch import ( + DISCOUNT_DOCTYPES, + LAST_MODIFIED_DATE_THRESHOLD, +) + + +def execute(): + for doctype in DISCOUNT_DOCTYPES: + documents = frappe.get_all( + doctype, + { + "docstatus": 0, + "modified": [">", LAST_MODIFIED_DATE_THRESHOLD], + "discount_amount": ["is", "set"], + }, + [ + "name", + "additional_discount_percentage", + "discount_amount", + "apply_discount_on", + "grand_total", + "net_total", + ], + ) + + if not documents: + continue + + precision = get_field_precision(frappe.get_meta(doctype).get_field("additional_discount_percentage")) + mismatched_documents = [] + + for doc in documents: + discount_applied_on = scrub(doc.apply_discount_on) + + calculated_discount_amount = flt( + doc.additional_discount_percentage * doc.get(discount_applied_on) / 100, + precision, + ) + + if calculated_discount_amount != doc.discount_amount: + mismatched_documents.append(doc.name) + + if mismatched_documents: + frappe.db.set_value( + doctype, + { + "name": ["in", mismatched_documents], + }, + "additional_discount_percentage", + 0, + ) From b3eb49d39d3b3051f30b1969307d1924a08ff78f Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Mon, 9 Jun 2025 11:13:59 +0530 Subject: [PATCH 2/9] feat: report to verify discount amount mismatch (cherry picked from commit 62dd6df24f4ed8e48b5cd8493c8b6082bbe12501) --- .../calculated_discount_mismatch/__init__.py | 0 .../calculated_discount_mismatch.js | 13 +++ .../calculated_discount_mismatch.json | 38 ++++++++ .../calculated_discount_mismatch.py | 86 +++++++++++++++++++ erpnext/patches.txt | 1 + 5 files changed, 138 insertions(+) create mode 100644 erpnext/accounts/report/calculated_discount_mismatch/__init__.py create mode 100644 erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js create mode 100644 erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.json create mode 100644 erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py diff --git a/erpnext/accounts/report/calculated_discount_mismatch/__init__.py b/erpnext/accounts/report/calculated_discount_mismatch/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js new file mode 100644 index 00000000000..b17e1e335c3 --- /dev/null +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js @@ -0,0 +1,13 @@ +// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Calculated Discount Mismatch"] = { + filters: [ + // { + // "fieldname": "my_filter", + // "label": __("My Filter"), + // "fieldtype": "Data", + // "reqd": 1, + // }, + ], +}; diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.json b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.json new file mode 100644 index 00000000000..84f081303e0 --- /dev/null +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.json @@ -0,0 +1,38 @@ +{ + "add_total_row": 0, + "add_translate_data": 0, + "columns": [], + "creation": "2025-06-06 17:09:50.681090", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "letterhead": null, + "modified": "2025-06-06 18:09:18.221911", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Calculated Discount Mismatch", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Version", + "report_name": "Calculated Discount Mismatch", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Accounts User" + } + ], + "timeout": 0 +} diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py new file mode 100644 index 00000000000..a51a3c57071 --- /dev/null +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -0,0 +1,86 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ + +DISCOUNT_DOCTYPES = frozenset( + ( + "POS Invoice", + "Purchase Invoice", + "Sales Invoice", + "Purchase Order", + "Supplier Quotation", + "Quotation", + "Sales Order", + "Delivery Note", + "Purchase Receipt", + ) +) +LAST_MODIFIED_DATE_THRESHOLD = "2025-05-30" + + +def execute(filters: dict | None = None): + """Return columns and data for the report. + + This is the main entry point for the report. It accepts the filters as a + dictionary and should return columns and data. It is called by the framework + every time the report is refreshed or a filter is updated. + """ + columns = get_columns() + data = get_data() + + return columns, data + + +def get_columns() -> list[dict]: + """Return columns for the report. + + One field definition per column, just like a DocType field definition. + """ + return [ + { + "label": _("Doctype"), + "fieldname": "doctype", + "fieldtype": "Data", + "width": 150, + }, + { + "label": _("Document Name"), + "fieldname": "document_name", + "fieldtype": "Dynamic Link", + "options": "doctype", + "width": 200, + }, + ] + + +def get_data() -> list[list]: + """Return data for the report. + + The report data is a list of rows, with each row being a list of cell values. + """ + data = [] + VERSION = frappe.qb.DocType("Version") + + result = ( + frappe.qb.from_(VERSION) + .select(VERSION.ref_doctype, VERSION.docname, VERSION.data, VERSION.name) + .where(VERSION.modified > LAST_MODIFIED_DATE_THRESHOLD) + .where(VERSION.ref_doctype.isin(list(DISCOUNT_DOCTYPES))) + .run(as_dict=True) + ) + + for row in result: + changed_data = {entry[0]: entry for entry in frappe.parse_json(row.data).get("changed", [])} + + docstatus = changed_data.get("docstatus") + if not docstatus or docstatus[2] != 1: + continue + + if "discount_amount" not in changed_data: + continue + + data.append({"doctype": row.ref_doctype, "document_name": row.docname}) + + return data diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 57e5465f8d0..edf91c225d7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -408,3 +408,4 @@ erpnext.patches.v15_0.set_cancelled_status_to_cancelled_pos_invoice erpnext.patches.v15_0.rename_group_by_to_categorize_by_in_custom_reports erpnext.patches.v14_0.update_full_name_in_contract erpnext.patches.v15_0.drop_sle_indexes +erpnext.patches.v15_0.set_additional_discount_percentage \ No newline at end of file From 5237ff8d945644ae32f52e21da076a7d01b5621a Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Mon, 9 Jun 2025 13:52:19 +0530 Subject: [PATCH 3/9] fix: changes to report and patch (cherry picked from commit daad6137f832c628de54e28223d47f5dd66d4456) --- .../calculated_discount_mismatch.js | 20 +-- .../calculated_discount_mismatch.py | 168 ++++++++++++++---- erpnext/patches.txt | 2 +- .../set_additional_discount_percentage.py | 56 ------ ...ncorrect_additional_discount_percentage.py | 87 +++++++++ 5 files changed, 229 insertions(+), 104 deletions(-) delete mode 100644 erpnext/patches/v15_0/set_additional_discount_percentage.py create mode 100644 erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js index b17e1e335c3..21d88d2e546 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.js @@ -1,13 +1,13 @@ // Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.query_reports["Calculated Discount Mismatch"] = { - filters: [ - // { - // "fieldname": "my_filter", - // "label": __("My Filter"), - // "fieldtype": "Data", - // "reqd": 1, - // }, - ], -}; +// frappe.query_reports["Calculated Discount Mismatch"] = { +// filters: [ +// { +// "fieldname": "my_filter", +// "label": __("My Filter"), +// "fieldtype": "Data", +// "reqd": 1, +// }, +// ], +// }; diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py index a51a3c57071..30d13c87afc 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -1,10 +1,14 @@ # Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import json + import frappe from frappe import _ +from frappe.query_builder import Order, Tuple +from frappe.utils import flt -DISCOUNT_DOCTYPES = frozenset( +AFFECTED_DOCTYPES = frozenset( ( "POS Invoice", "Purchase Invoice", @@ -20,67 +24,157 @@ DISCOUNT_DOCTYPES = frozenset( LAST_MODIFIED_DATE_THRESHOLD = "2025-05-30" -def execute(filters: dict | None = None): - """Return columns and data for the report. - - This is the main entry point for the report. It accepts the filters as a - dictionary and should return columns and data. It is called by the framework - every time the report is refreshed or a filter is updated. - """ +def execute(): columns = get_columns() data = get_data() return columns, data -def get_columns() -> list[dict]: - """Return columns for the report. - - One field definition per column, just like a DocType field definition. - """ +def get_columns(): return [ { - "label": _("Doctype"), "fieldname": "doctype", - "fieldtype": "Data", + "label": _("Transaction Type"), + "fieldtype": "Link", + "options": "DocType", + "width": 120, + }, + { + "fieldname": "docname", + "label": _("Transaction Name"), + "fieldtype": "Dynamic Link", + "options": "doctype", "width": 150, }, { - "label": _("Document Name"), - "fieldname": "document_name", - "fieldtype": "Dynamic Link", - "options": "doctype", - "width": 200, + "fieldname": "currency", + "label": _("Currency"), + "fieldtype": "Link", + "options": "Currency", + }, + { + "fieldname": "actual_discount_percentage", + "label": _("Discount Percentage in Transaction"), + "fieldtype": "Percent", + "width": 180, + }, + { + "fieldname": "actual_discount_amount", + "label": _("Discount Amount in Transaction"), + "fieldtype": "Currency", + "options": "currency", + "width": 180, + }, + { + "fieldname": "suspected_discount_amount", + "label": _("Suspected Discount Amount"), + "fieldtype": "Currency", + "options": "currency", + "width": 180, + }, + { + "fieldname": "difference", + "label": _("Difference"), + "fieldtype": "Currency", + "options": "currency", + "width": 180, }, ] -def get_data() -> list[list]: - """Return data for the report. +def get_data(): + transactions_with_discount_percentage = {} + + for doctype in AFFECTED_DOCTYPES: + transactions = get_transactions_with_discount_percentage(doctype) + + for transaction in transactions: + transactions_with_discount_percentage[(doctype, transaction.name)] = transaction + + if not transactions_with_discount_percentage: + return [] - The report data is a list of rows, with each row being a list of cell values. - """ - data = [] VERSION = frappe.qb.DocType("Version") - result = ( + versions = ( frappe.qb.from_(VERSION) - .select(VERSION.ref_doctype, VERSION.docname, VERSION.data, VERSION.name) - .where(VERSION.modified > LAST_MODIFIED_DATE_THRESHOLD) - .where(VERSION.ref_doctype.isin(list(DISCOUNT_DOCTYPES))) + .select(VERSION.ref_doctype, VERSION.docname, VERSION.data) + .where(VERSION.creation > LAST_MODIFIED_DATE_THRESHOLD) + .where(Tuple(VERSION.ref_doctype, VERSION.docname).isin(list(transactions_with_discount_percentage))) + .where( + VERSION.data.like('%"discount\\_amount"%') + | VERSION.data.like('%"additional\\_discount\\_percentage"%') + ) + .orderby(VERSION.creation, order=Order.desc) .run(as_dict=True) ) - for row in result: - changed_data = {entry[0]: entry for entry in frappe.parse_json(row.data).get("changed", [])} + if not versions: + return [] - docstatus = changed_data.get("docstatus") - if not docstatus or docstatus[2] != 1: - continue + version_map = {} + for version in versions: + key = (version.ref_doctype, version.docname) + if key not in version_map: + version_map[key] = [] - if "discount_amount" not in changed_data: - continue + version_map[key].append(version.data) - data.append({"doctype": row.ref_doctype, "document_name": row.docname}) + data = [] + for doc, versions in version_map.items(): + for version_data in versions: + if '"additional_discount_percentage"' in version_data: + # don't consider doc if additional_discount_percentage is changed in newest version + break + + version_data = json.loads(version_data) + changed_values = version_data.get("changed") + if not changed_values: + continue + + discount_values = next((row for row in changed_values if row[0] == "discount_amount"), None) + if not discount_values: + continue + + old = discount_values[1] + new = discount_values[2] + doc_values = transactions_with_discount_percentage.get(doc) + if new != doc_values.discount_amount: + # if the discount amount in the version is not equal to the current value, skip + break + + data.append( + { + "doctype": doc[0], + "docname": doc[1], + "currency": doc_values.currency, + "actual_discount_percentage": doc_values.additional_discount_percentage, + "actual_discount_amount": new, + "suspected_discount_amount": old, + "difference": flt(old - new, 9), + } + ) + break return data + + +def get_transactions_with_discount_percentage(doctype): + transactions = frappe.get_all( + doctype, + fields=[ + "name", + "currency", + "additional_discount_percentage", + "discount_amount", + ], + filters={ + "docstatus": 1, + "additional_discount_percentage": [">", 0], + "discount_amount": ["!=", 0], + "modified": [">", LAST_MODIFIED_DATE_THRESHOLD], + }, + ) + + return transactions diff --git a/erpnext/patches.txt b/erpnext/patches.txt index edf91c225d7..ca3e61f5d26 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -262,6 +262,7 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles execute:frappe.rename_doc("Report", "TDS Payable Monthly", "Tax Withholding Details", force=True) erpnext.patches.v14_0.update_proprietorship_to_individual erpnext.patches.v15_0.rename_subcontracting_fields +erpnext.patches.v15_0.unset_incorrect_additional_discount_percentage [post_model_sync] erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets @@ -408,4 +409,3 @@ erpnext.patches.v15_0.set_cancelled_status_to_cancelled_pos_invoice erpnext.patches.v15_0.rename_group_by_to_categorize_by_in_custom_reports erpnext.patches.v14_0.update_full_name_in_contract erpnext.patches.v15_0.drop_sle_indexes -erpnext.patches.v15_0.set_additional_discount_percentage \ No newline at end of file diff --git a/erpnext/patches/v15_0/set_additional_discount_percentage.py b/erpnext/patches/v15_0/set_additional_discount_percentage.py deleted file mode 100644 index 2015bfd6c32..00000000000 --- a/erpnext/patches/v15_0/set_additional_discount_percentage.py +++ /dev/null @@ -1,56 +0,0 @@ -import frappe -from frappe import scrub -from frappe.model.meta import get_field_precision -from frappe.utils import flt - -from erpnext.accounts.report.calculated_discount_mismatch.calculated_discount_mismatch import ( - DISCOUNT_DOCTYPES, - LAST_MODIFIED_DATE_THRESHOLD, -) - - -def execute(): - for doctype in DISCOUNT_DOCTYPES: - documents = frappe.get_all( - doctype, - { - "docstatus": 0, - "modified": [">", LAST_MODIFIED_DATE_THRESHOLD], - "discount_amount": ["is", "set"], - }, - [ - "name", - "additional_discount_percentage", - "discount_amount", - "apply_discount_on", - "grand_total", - "net_total", - ], - ) - - if not documents: - continue - - precision = get_field_precision(frappe.get_meta(doctype).get_field("additional_discount_percentage")) - mismatched_documents = [] - - for doc in documents: - discount_applied_on = scrub(doc.apply_discount_on) - - calculated_discount_amount = flt( - doc.additional_discount_percentage * doc.get(discount_applied_on) / 100, - precision, - ) - - if calculated_discount_amount != doc.discount_amount: - mismatched_documents.append(doc.name) - - if mismatched_documents: - frappe.db.set_value( - doctype, - { - "name": ["in", mismatched_documents], - }, - "additional_discount_percentage", - 0, - ) diff --git a/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py b/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py new file mode 100644 index 00000000000..40be90f1396 --- /dev/null +++ b/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py @@ -0,0 +1,87 @@ +import frappe +from frappe import scrub +from frappe.model.meta import get_field_precision +from frappe.utils import flt +from semantic_version import Version + +from erpnext.accounts.report.calculated_discount_mismatch.calculated_discount_mismatch import ( + AFFECTED_DOCTYPES, + LAST_MODIFIED_DATE_THRESHOLD, +) + + +def execute(): + # run this patch only if erpnext version before update is v15.64.0 or higher + version, git_branch = frappe.db.get_value( + "Installed Application", + {"app_name": "erpnext"}, + ["app_version", "git_branch"], + ) + + semantic_version = get_semantic_version(version) + if semantic_version and ( + semantic_version.major < 15 or (git_branch == "version-15" and semantic_version.minor < 64) + ): + return + + for doctype in AFFECTED_DOCTYPES: + meta = frappe.get_meta(doctype) + filters = { + "modified": [">", LAST_MODIFIED_DATE_THRESHOLD], + "additional_discount_percentage": [">", 0], + "discount_amount": ["!=", 0], + } + + # can't reverse calculate grand_total if shipping rule is set + if meta.has_field("shipping_rule"): + filters["shipping_rule"] = ["is", "not set"] + + documents = frappe.get_all( + doctype, + fields=[ + "name", + "additional_discount_percentage", + "discount_amount", + "apply_discount_on", + "grand_total", + "net_total", + ], + filters=filters, + ) + + if not documents: + continue + + precision = get_field_precision(frappe.get_meta(doctype).get_field("additional_discount_percentage")) + mismatched_documents = [] + + for doc in documents: + # we need grand_total before applying discount + doc.grand_total += doc.discount_amount + discount_applied_on = scrub(doc.apply_discount_on) + calculated_discount_amount = flt( + doc.additional_discount_percentage * doc.get(discount_applied_on) / 100, + precision, + ) + + # if difference is more than 0.02 (based on precision), unset the additional discount percentage + if abs(calculated_discount_amount - doc.discount_amount) > 2 / (10**precision): + mismatched_documents.append(doc.name) + + if mismatched_documents: + # changing the discount percentage has no accounting effect + # so we can safely set it to 0 in the database + frappe.db.set_value( + doctype, + {"name": ["in", mismatched_documents]}, + "additional_discount_percentage", + 0, + update_modified=False, + ) + + +def get_semantic_version(version): + try: + return Version(version) + except Exception: + pass From d24c2c4ccafec82c185af05eced2daac00da9a2f Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Mon, 9 Jun 2025 17:49:40 +0530 Subject: [PATCH 4/9] fix: ensure proper float conversion for discount values (cherry picked from commit 3dcb801a3791a67cf8f38f84700472457a5835e3) --- .../calculated_discount_mismatch.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py index 30d13c87afc..e3cba25e8f4 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -24,7 +24,7 @@ AFFECTED_DOCTYPES = frozenset( LAST_MODIFIED_DATE_THRESHOLD = "2025-05-30" -def execute(): +def execute(filters=None): columns = get_columns() data = get_data() @@ -137,8 +137,8 @@ def get_data(): if not discount_values: continue - old = discount_values[1] - new = discount_values[2] + old = flt(discount_values[1][2:]) + new = flt(discount_values[2][2:]) doc_values = transactions_with_discount_percentage.get(doc) if new != doc_values.discount_amount: # if the discount amount in the version is not equal to the current value, skip From f27e591d88c19c0fe7e7a922b5d4e19d43986ece Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Mon, 9 Jun 2025 18:06:16 +0530 Subject: [PATCH 5/9] fix: add change log for bug fix in Additional Discount functionality (cherry picked from commit 9120927a650aa93eaa43d2d753363a67725d0a27) --- erpnext/change_log/v15/v15_64_0.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 erpnext/change_log/v15/v15_64_0.md diff --git a/erpnext/change_log/v15/v15_64_0.md b/erpnext/change_log/v15/v15_64_0.md new file mode 100644 index 00000000000..67f50707203 --- /dev/null +++ b/erpnext/change_log/v15/v15_64_0.md @@ -0,0 +1,11 @@ +There was a bug in the **Additional Discount** functionality of ERPNext in **v15.64.0**. This has since been fixed. + +**If you've updated from a version older than v15.64.0, no action is needed on your side.** + +If you're updating from v15.64.0, the **Additional Discount Amount** in some transactions may differ from the value you entered. This only affects cases where **Additional Discount Amount** is manually entered. If it is computed from **Additional Discount Percentage** entered by you, there shouldn't be any issue. + +This report can help identify such transactions: [Calculated Discount Mismatch](/app/query-report/Calculated%20Discount%20Mismatch) + +Please review and amend these as necessary. + +We apologize for the inconvenience caused. \ No newline at end of file From 06ea957ae5e398a58237c77e63b5108e97acef9c Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Mon, 9 Jun 2025 18:43:58 +0530 Subject: [PATCH 6/9] fix: test case to verify correct setting of discount amount and percentage (cherry picked from commit 3f0c5be5d9d5efde5fd9086791c503431dc559a9) --- .../doctype/purchase_invoice/test_purchase_invoice.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 055f5ca9676..99eb1de2cf8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2806,6 +2806,17 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): # Test 4 - Since this PI is overbilled by 130% and only 120% is allowed, it will fail self.assertRaises(frappe.ValidationError, pi.submit) + def test_discount_percentage_not_set_when_amount_is_manually_set(self): + pi = make_purchase_invoice(do_not_save=True) + discount_amount = 7 + pi.discount_amount = discount_amount + pi.save() + self.assertEqual(pi.additional_discount_percentage, None) + pi.set_posting_time = 1 + pi.posting_date = add_days(today(), -1) + pi.save() + self.assertEqual(pi.discount_amount, discount_amount) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 78c63869e0043a5b76338ef810d4e3cd659d1b40 Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Mon, 9 Jun 2025 19:13:03 +0530 Subject: [PATCH 7/9] fix: changes in report (cherry picked from commit 33e793354cf8220b8462a0bce8efee43941882c1) --- .../calculated_discount_mismatch.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py index e3cba25e8f4..48d78eb9d6f 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -7,6 +7,7 @@ import frappe from frappe import _ from frappe.query_builder import Order, Tuple from frappe.utils import flt +from frappe.utils.formatters import format_value AFFECTED_DOCTYPES = frozenset( ( @@ -62,22 +63,13 @@ def get_columns(): { "fieldname": "actual_discount_amount", "label": _("Discount Amount in Transaction"), - "fieldtype": "Currency", - "options": "currency", + "fieldtype": "Data", "width": 180, }, { "fieldname": "suspected_discount_amount", "label": _("Suspected Discount Amount"), - "fieldtype": "Currency", - "options": "currency", - "width": 180, - }, - { - "fieldname": "difference", - "label": _("Difference"), - "fieldtype": "Currency", - "options": "currency", + "fieldtype": "Data", "width": 180, }, ] @@ -122,6 +114,9 @@ def get_data(): version_map[key].append(version.data) data = [] + discount_amount_field_map = { + doctype: frappe.get_meta(doctype).get_field("discount_amount") for doctype in AFFECTED_DOCTYPES + } for doc, versions in version_map.items(): for version_data in versions: if '"additional_discount_percentage"' in version_data: @@ -137,22 +132,28 @@ def get_data(): if not discount_values: continue - old = flt(discount_values[1][2:]) - new = flt(discount_values[2][2:]) + old = discount_values[1] + new = discount_values[2] + doctype = doc[0] doc_values = transactions_with_discount_percentage.get(doc) - if new != doc_values.discount_amount: + formatted_discount_amount = format_value( + doc_values.discount_amount, + df=discount_amount_field_map[doctype], + currency=doc_values.currency, + ) + + if new != formatted_discount_amount: # if the discount amount in the version is not equal to the current value, skip break data.append( { - "doctype": doc[0], - "docname": doc[1], + "doctype": doctype, + "docname": doc_values.name, "currency": doc_values.currency, "actual_discount_percentage": doc_values.additional_discount_percentage, "actual_discount_amount": new, "suspected_discount_amount": old, - "difference": flt(old - new, 9), } ) break From 35035c2a31a9d81295d90900907b8ea28e275644 Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Mon, 9 Jun 2025 19:13:53 +0530 Subject: [PATCH 8/9] fix: remove currency col (cherry picked from commit 9bf9b34ac4ff1bbf0e9fb395e9616c99ac9805d1) --- .../calculated_discount_mismatch.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py index 48d78eb9d6f..d8005761e92 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -48,12 +48,6 @@ def get_columns(): "options": "doctype", "width": 150, }, - { - "fieldname": "currency", - "label": _("Currency"), - "fieldtype": "Link", - "options": "Currency", - }, { "fieldname": "actual_discount_percentage", "label": _("Discount Percentage in Transaction"), @@ -150,7 +144,6 @@ def get_data(): { "doctype": doctype, "docname": doc_values.name, - "currency": doc_values.currency, "actual_discount_percentage": doc_values.additional_discount_percentage, "actual_discount_amount": new, "suspected_discount_amount": old, From 59dd5fee2626223bd1b75cd30a47b7e20160bcc4 Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Mon, 9 Jun 2025 19:20:40 +0530 Subject: [PATCH 9/9] fix: fieldtype to Currency for discount amounts (cherry picked from commit f781a39dbef72667eda9d248344ec7e9f11acdb0) --- .../calculated_discount_mismatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py index d8005761e92..4017fca2b38 100644 --- a/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py +++ b/erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py @@ -57,13 +57,13 @@ def get_columns(): { "fieldname": "actual_discount_amount", "label": _("Discount Amount in Transaction"), - "fieldtype": "Data", + "fieldtype": "Currency", "width": 180, }, { "fieldname": "suspected_discount_amount", "label": _("Suspected Discount Amount"), - "fieldtype": "Data", + "fieldtype": "Currency", "width": 180, }, ]