From 47cc8ab6c6e6ad3cf13ba750e9410696be827c23 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:22:54 +0530 Subject: [PATCH 01/36] fix: opening_accumulated_depreciation and precision in charts --- erpnext/assets/doctype/asset/asset.js | 39 ++++++++++--------- erpnext/assets/doctype/asset/asset.py | 8 ++-- .../fixed_asset_register.py | 10 ++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 276cc925208..bffe1995457 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -210,52 +210,53 @@ frappe.ui.form.on('Asset', { return } - var x_intervals = [frm.doc.purchase_date]; + var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })]; var asset_values = [frm.doc.gross_purchase_amount]; - var last_depreciation_date = frm.doc.purchase_date; - - if(frm.doc.opening_accumulated_depreciation) { - last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date, - -1*frm.doc.frequency_of_depreciation); - - x_intervals.push(last_depreciation_date); - asset_values.push(flt(frm.doc.gross_purchase_amount) - - flt(frm.doc.opening_accumulated_depreciation)); - } if(frm.doc.calculate_depreciation) { + if(frm.doc.opening_accumulated_depreciation) { + var depreciation_date = frappe.datetime.add_months( + frm.doc.finance_books[0].depreciation_start_date, + -1 * frm.doc.finance_books[0].frequency_of_depreciation + ); + x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + $.each(frm.doc.schedules || [], function(i, v) { - x_intervals.push(v.schedule_date); - var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount); + x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' })); + var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount')); if(v.journal_entry) { - last_depreciation_date = v.schedule_date; asset_values.push(asset_value); } else { if (in_list(["Scrapped", "Sold"], frm.doc.status)) { asset_values.push(null); } else { - asset_values.push(asset_value) + asset_values.push(asset_value); } } }); } else { + if(frm.doc.opening_accumulated_depreciation) { + x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + let depr_entries = (await frappe.call({ method: "get_manual_depreciation_entries", doc: frm.doc, })).message; $.each(depr_entries || [], function(i, v) { - x_intervals.push(v.posting_date); - last_depreciation_date = v.posting_date; + x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); let last_asset_value = asset_values[asset_values.length - 1] asset_values.push(last_asset_value - v.value); }); } if(in_list(["Scrapped", "Sold"], frm.doc.status)) { - x_intervals.push(frm.doc.disposal_date); + x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' })); asset_values.push(0); - last_depreciation_date = frm.doc.disposal_date; } frm.dashboard.render_graph({ diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index cd9f3b420d1..d3eb958fa6d 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -693,14 +693,16 @@ class Asset(AccountsController): def get_value_after_depreciation(self, finance_book=None): if not self.calculate_depreciation: - return self.value_after_depreciation + return flt(self.value_after_depreciation, self.precision("gross_purchase_amount")) if not finance_book: - return self.get("finance_books")[0].value_after_depreciation + return flt( + self.get("finance_books")[0].value_after_depreciation, self.precision("gross_purchase_amount") + ) for row in self.get("finance_books"): if finance_book == row.finance_book: - return row.value_after_depreciation + return flt(row.value_after_depreciation, self.precision("gross_purchase_amount")) def get_default_finance_book_idx(self): if not self.get("default_finance_book") and self.company: diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 5bfd4840d73..0ab3e16e424 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import cstr, formatdate, getdate +from frappe.utils import cstr, flt, formatdate, getdate from erpnext.accounts.report.financial_statements import ( get_fiscal_year_data, @@ -102,13 +102,9 @@ def get_data(filters): ] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) - finance_book_filter = ("is", "not set") - if filters.finance_book: - finance_book_filter = ("=", filters.finance_book) - assets_linked_to_fb = frappe.db.get_all( doctype="Asset Finance Book", - filters={"finance_book": finance_book_filter}, + filters={"finance_book": filters.finance_book or ("is", "not set")}, pluck="parent", ) @@ -194,7 +190,7 @@ def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters): else: depr_amount = get_manual_depreciation_amount_of_asset(asset, filters) - return depr_amount + return flt(depr_amount, 2) def get_finance_book_value_map(filters): From a220dc0c9c694a9213189b96be8eb9431457280c Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:28:46 +0530 Subject: [PATCH 02/36] chore: break look if je processed --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index b9507b73e29..e52fd6225fb 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -336,6 +336,8 @@ class JournalEntry(AccountsController): finance_books.db_update() asset.set_status() + + break else: depr_value = d.debit or d.credit From d117de7813ea8e773f3b8d5262415ac18fda870f Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 14 Feb 2023 04:59:27 +0100 Subject: [PATCH 03/36] feat: translate fixtures during runtime, not installation (#33996) feat: install untranslated fixtures from files Co-authored-by: Deepesh Garg (cherry picked from commit 770369e5c1dc43b26f41e118432d9be923ba61ef) --- .../crm/doctype/lead_source/lead_source.json | 7 +- .../crm/doctype/sales_stage/sales_stage.json | 7 +- .../v11_0/update_sales_partner_type.py | 7 +- .../doctype/industry_type/industry_type.json | 167 ++++++------------ .../sales_partner_type.json | 123 ++++--------- .../doctype/designation/designation.json | 5 +- .../setup/setup_wizard/data/designation.txt | 31 ++++ .../setup/setup_wizard/data/industry_type.py | 57 ------ .../setup/setup_wizard/data/industry_type.txt | 51 ++++++ .../setup/setup_wizard/data/lead_source.txt | 10 ++ .../setup_wizard/data/sales_partner_type.txt | 7 + .../setup/setup_wizard/data/sales_stage.txt | 8 + .../operations/install_fixtures.py | 79 +++------ 13 files changed, 239 insertions(+), 320 deletions(-) create mode 100644 erpnext/setup/setup_wizard/data/designation.txt delete mode 100644 erpnext/setup/setup_wizard/data/industry_type.py create mode 100644 erpnext/setup/setup_wizard/data/industry_type.txt create mode 100644 erpnext/setup/setup_wizard/data/lead_source.txt create mode 100644 erpnext/setup/setup_wizard/data/sales_partner_type.txt create mode 100644 erpnext/setup/setup_wizard/data/sales_stage.txt diff --git a/erpnext/crm/doctype/lead_source/lead_source.json b/erpnext/crm/doctype/lead_source/lead_source.json index 723c6d993d7..c3cedcc7a63 100644 --- a/erpnext/crm/doctype/lead_source/lead_source.json +++ b/erpnext/crm/doctype/lead_source/lead_source.json @@ -26,10 +26,11 @@ } ], "links": [], - "modified": "2021-02-08 12:51:48.971517", + "modified": "2023-02-10 00:51:44.973957", "modified_by": "Administrator", "module": "CRM", "name": "Lead Source", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -58,5 +59,7 @@ ], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [], + "translated_doctype": 1 } \ No newline at end of file diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 77aa559b771..caf8ff5b36b 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -18,10 +18,11 @@ } ], "links": [], - "modified": "2020-05-20 12:22:01.866472", + "modified": "2023-02-10 01:40:23.713390", "modified_by": "Administrator", "module": "CRM", "name": "Sales Stage", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -40,5 +41,7 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1 + "states": [], + "track_changes": 1, + "translated_doctype": 1 } \ No newline at end of file diff --git a/erpnext/patches/v11_0/update_sales_partner_type.py b/erpnext/patches/v11_0/update_sales_partner_type.py index 2d37fd69b19..72fd424b245 100644 --- a/erpnext/patches/v11_0/update_sales_partner_type.py +++ b/erpnext/patches/v11_0/update_sales_partner_type.py @@ -1,16 +1,17 @@ import frappe -from frappe import _ def execute(): - from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type + from erpnext.setup.setup_wizard.operations.install_fixtures import read_lines frappe.reload_doc("selling", "doctype", "sales_partner_type") frappe.local.lang = frappe.db.get_default("lang") or "en" + default_sales_partner_type = read_lines("sales_partner_type.txt") + for s in default_sales_partner_type: - insert_sales_partner_type(_(s)) + insert_sales_partner_type(s) # get partner type in existing forms (customized) # and create a document if not created diff --git a/erpnext/selling/doctype/industry_type/industry_type.json b/erpnext/selling/doctype/industry_type/industry_type.json index 6c49f0f6dda..3c8ab8e47ae 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.json +++ b/erpnext/selling/doctype/industry_type/industry_type.json @@ -1,123 +1,68 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:industry", - "beta": 0, - "creation": "2012-03-27 14:36:09", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:industry", + "creation": "2012-03-27 14:36:09", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "industry" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "industry", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Industry", - "length": 0, - "no_copy": 0, - "oldfieldname": "industry", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "industry", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Industry", + "oldfieldname": "industry", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-flag", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2020-09-18 17:26:09.703215", - "modified_by": "Administrator", - "module": "Selling", - "name": "Industry Type", - "owner": "Administrator", + ], + "icon": "fa fa-flag", + "idx": 1, + "links": [], + "modified": "2023-02-10 03:14:40.735763", + "modified_by": "Administrator", + "module": "Selling", + "name": "Industry Type", + "naming_rule": "By fieldname", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User" + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Master Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "translated_doctype": 1 } \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json index e7dd0d84a0a..a9b500a625f 100644 --- a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json +++ b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json @@ -1,94 +1,47 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:sales_partner_type", - "beta": 0, - "creation": "2018-06-11 13:15:57.404716", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:sales_partner_type", + "creation": "2018-06-11 13:15:57.404716", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "sales_partner_type" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_partner_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Sales Partner Type", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "sales_partner_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Sales Partner Type", + "reqd": 1, + "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-06-11 13:45:13.554307", - "modified_by": "Administrator", - "module": "Selling", - "name": "Sales Partner Type", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2023-02-10 01:00:20.110800", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Partner Type", + "naming_rule": "By fieldname", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "translated_doctype": 1 } \ No newline at end of file diff --git a/erpnext/setup/doctype/designation/designation.json b/erpnext/setup/doctype/designation/designation.json index 2cbbb04ed91..a5b2ac9128a 100644 --- a/erpnext/setup/doctype/designation/designation.json +++ b/erpnext/setup/doctype/designation/designation.json @@ -31,7 +31,7 @@ "icon": "fa fa-bookmark", "idx": 1, "links": [], - "modified": "2022-06-28 17:10:26.853753", + "modified": "2023-02-10 01:53:41.319386", "modified_by": "Administrator", "module": "Setup", "name": "Designation", @@ -58,5 +58,6 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "ASC", - "states": [] + "states": [], + "translated_doctype": 1 } \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/designation.txt b/erpnext/setup/setup_wizard/data/designation.txt new file mode 100644 index 00000000000..4c6d7bdea8a --- /dev/null +++ b/erpnext/setup/setup_wizard/data/designation.txt @@ -0,0 +1,31 @@ +Accountant +Administrative Assistant +Administrative Officer +Analyst +Associate +Business Analyst +Business Development Manager +Consultant +Chief Executive Officer +Chief Financial Officer +Chief Operating Officer +Chief Technology Officer +Customer Service Representative +Designer +Engineer +Executive Assistant +Finance Manager +HR Manager +Head of Marketing and Sales +Manager +Managing Director +Marketing Manager +Marketing Specialist +President +Product Manager +Project Manager +Researcher +Sales Representative +Secretary +Software Developer +Vice President diff --git a/erpnext/setup/setup_wizard/data/industry_type.py b/erpnext/setup/setup_wizard/data/industry_type.py deleted file mode 100644 index 0bc3f32eb09..00000000000 --- a/erpnext/setup/setup_wizard/data/industry_type.py +++ /dev/null @@ -1,57 +0,0 @@ -from frappe import _ - - -def get_industry_types(): - return [ - _("Accounting"), - _("Advertising"), - _("Aerospace"), - _("Agriculture"), - _("Airline"), - _("Apparel & Accessories"), - _("Automotive"), - _("Banking"), - _("Biotechnology"), - _("Broadcasting"), - _("Brokerage"), - _("Chemical"), - _("Computer"), - _("Consulting"), - _("Consumer Products"), - _("Cosmetics"), - _("Defense"), - _("Department Stores"), - _("Education"), - _("Electronics"), - _("Energy"), - _("Entertainment & Leisure"), - _("Executive Search"), - _("Financial Services"), - _("Food, Beverage & Tobacco"), - _("Grocery"), - _("Health Care"), - _("Internet Publishing"), - _("Investment Banking"), - _("Legal"), - _("Manufacturing"), - _("Motion Picture & Video"), - _("Music"), - _("Newspaper Publishers"), - _("Online Auctions"), - _("Pension Funds"), - _("Pharmaceuticals"), - _("Private Equity"), - _("Publishing"), - _("Real Estate"), - _("Retail & Wholesale"), - _("Securities & Commodity Exchanges"), - _("Service"), - _("Soap & Detergent"), - _("Software"), - _("Sports"), - _("Technology"), - _("Telecommunications"), - _("Television"), - _("Transportation"), - _("Venture Capital"), - ] diff --git a/erpnext/setup/setup_wizard/data/industry_type.txt b/erpnext/setup/setup_wizard/data/industry_type.txt new file mode 100644 index 00000000000..eadc689e312 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/industry_type.txt @@ -0,0 +1,51 @@ +Accounting +Advertising +Aerospace +Agriculture +Airline +Apparel & Accessories +Automotive +Banking +Biotechnology +Broadcasting +Brokerage +Chemical +Computer +Consulting +Consumer Products +Cosmetics +Defense +Department Stores +Education +Electronics +Energy +Entertainment & Leisure +Executive Search +Financial Services +Food, Beverage & Tobacco +Grocery +Health Care +Internet Publishing +Investment Banking +Legal +Manufacturing +Motion Picture & Video +Music +Newspaper Publishers +Online Auctions +Pension Funds +Pharmaceuticals +Private Equity +Publishing +Real Estate +Retail & Wholesale +Securities & Commodity Exchanges +Service +Soap & Detergent +Software +Sports +Technology +Telecommunications +Television +Transportation +Venture Capital diff --git a/erpnext/setup/setup_wizard/data/lead_source.txt b/erpnext/setup/setup_wizard/data/lead_source.txt new file mode 100644 index 00000000000..00ca1808bb5 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/lead_source.txt @@ -0,0 +1,10 @@ +Existing Customer +Reference +Advertisement +Cold Calling +Exhibition +Supplier Reference +Mass Mailing +Customer's Vendor +Campaign +Walk In diff --git a/erpnext/setup/setup_wizard/data/sales_partner_type.txt b/erpnext/setup/setup_wizard/data/sales_partner_type.txt new file mode 100644 index 00000000000..68e9b9ac732 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/sales_partner_type.txt @@ -0,0 +1,7 @@ +Channel Partner +Distributor +Dealer +Agent +Retailer +Implementation Partner +Reseller diff --git a/erpnext/setup/setup_wizard/data/sales_stage.txt b/erpnext/setup/setup_wizard/data/sales_stage.txt new file mode 100644 index 00000000000..2808ce79855 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/sales_stage.txt @@ -0,0 +1,8 @@ +Prospecting +Qualification +Needs Analysis +Value Proposition +Identifying Decision Makers +Perception Analysis +Proposal/Price Quote +Negotiation/Review diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 1f8c0d6a1ca..6bc17718ae0 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -4,6 +4,7 @@ import json import os +from pathlib import Path import frappe from frappe import _ @@ -16,28 +17,10 @@ from frappe.utils import cstr, getdate from erpnext.accounts.doctype.account.account import RootNotEditable from erpnext.regional.address_template.setup import set_up_address_templates -default_lead_sources = [ - "Existing Customer", - "Reference", - "Advertisement", - "Cold Calling", - "Exhibition", - "Supplier Reference", - "Mass Mailing", - "Customer's Vendor", - "Campaign", - "Walk In", -] -default_sales_partner_type = [ - "Channel Partner", - "Distributor", - "Dealer", - "Agent", - "Retailer", - "Implementation Partner", - "Reseller", -] +def read_lines(filename: str) -> list[str]: + """Return a list of lines from a file in the data directory.""" + return (Path(__file__).parent.parent / "data" / filename).read_text().splitlines() def install(country=None): @@ -85,7 +68,11 @@ def install(country=None): # Stock Entry Type {"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"}, {"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"}, - {"doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer"}, + { + "doctype": "Stock Entry Type", + "name": "Material Transfer", + "purpose": "Material Transfer", + }, {"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"}, {"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"}, { @@ -103,22 +90,6 @@ def install(country=None): "name": "Material Consumption for Manufacture", "purpose": "Material Consumption for Manufacture", }, - # Designation - {"doctype": "Designation", "designation_name": _("CEO")}, - {"doctype": "Designation", "designation_name": _("Manager")}, - {"doctype": "Designation", "designation_name": _("Analyst")}, - {"doctype": "Designation", "designation_name": _("Engineer")}, - {"doctype": "Designation", "designation_name": _("Accountant")}, - {"doctype": "Designation", "designation_name": _("Secretary")}, - {"doctype": "Designation", "designation_name": _("Associate")}, - {"doctype": "Designation", "designation_name": _("Administrative Officer")}, - {"doctype": "Designation", "designation_name": _("Business Development Manager")}, - {"doctype": "Designation", "designation_name": _("HR Manager")}, - {"doctype": "Designation", "designation_name": _("Project Manager")}, - {"doctype": "Designation", "designation_name": _("Head of Marketing and Sales")}, - {"doctype": "Designation", "designation_name": _("Software Developer")}, - {"doctype": "Designation", "designation_name": _("Designer")}, - {"doctype": "Designation", "designation_name": _("Researcher")}, # territory: with two default territories, one for home country and one named Rest of the World { "doctype": "Territory", @@ -291,28 +262,18 @@ def install(country=None): {"doctype": "Market Segment", "market_segment": _("Lower Income")}, {"doctype": "Market Segment", "market_segment": _("Middle Income")}, {"doctype": "Market Segment", "market_segment": _("Upper Income")}, - # Sales Stages - {"doctype": "Sales Stage", "stage_name": _("Prospecting")}, - {"doctype": "Sales Stage", "stage_name": _("Qualification")}, - {"doctype": "Sales Stage", "stage_name": _("Needs Analysis")}, - {"doctype": "Sales Stage", "stage_name": _("Value Proposition")}, - {"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")}, - {"doctype": "Sales Stage", "stage_name": _("Perception Analysis")}, - {"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")}, - {"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}, # Warehouse Type {"doctype": "Warehouse Type", "name": "Transit"}, ] - from erpnext.setup.setup_wizard.data.industry_type import get_industry_types - - records += [{"doctype": "Industry Type", "industry": d} for d in get_industry_types()] - # records += [{"doctype":"Operation", "operation": d} for d in get_operations()] - records += [{"doctype": "Lead Source", "source_name": _(d)} for d in default_lead_sources] - - records += [ - {"doctype": "Sales Partner Type", "sales_partner_type": _(d)} for d in default_sales_partner_type - ] + for doctype, title_field, filename in ( + ("Designation", "designation_name", "designation.txt"), + ("Sales Stage", "stage_name", "sales_stage.txt"), + ("Industry Type", "industry", "industry_type.txt"), + ("Lead Source", "source_name", "lead_source.txt"), + ("Sales Partner Type", "sales_partner_type", "sales_partner_type.txt"), + ): + records += [{"doctype": doctype, title_field: title} for title in read_lines(filename)] base_path = frappe.get_app_path("erpnext", "stock", "doctype") response = frappe.read_file( @@ -397,7 +358,8 @@ def add_uom_data(): frappe.get_doc({"doctype": "UOM Category", "category_name": _(d.get("category"))}).db_insert() if not frappe.db.exists( - "UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))} + "UOM Conversion Factor", + {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}, ): frappe.get_doc( { @@ -535,7 +497,8 @@ def create_bank_account(args): company_name = args.get("company_name") bank_account_group = frappe.db.get_value( - "Account", {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name} + "Account", + {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name}, ) if bank_account_group: bank_account = frappe.get_doc( From 15898cc2ec5289ad3852cfe61ec64ff572c262d1 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Feb 2023 13:36:25 +0530 Subject: [PATCH 04/36] fix: update `reserved_qty` when `Sales Order` marked as `Hold` (cherry picked from commit d76759e06690a19dc1a8b4fb201d509cee1b47e9) --- erpnext/stock/stock_balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 14cedd2e8a9..439ed7a8e09 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -121,7 +121,7 @@ def get_reserved_qty(item_code, warehouse): and parenttype='Sales Order' and item_code != parent_item and exists (select * from `tabSales Order` so - where name = dnpi_in.parent and docstatus = 1 and status != 'Closed') + where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed')) ) dnpi) union (select stock_qty as dnpi_qty, qty as so_item_qty, @@ -131,7 +131,7 @@ def get_reserved_qty(item_code, warehouse): and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0) and exists(select * from `tabSales Order` so where so.name = so_item.parent and so.docstatus = 1 - and so.status != 'Closed')) + and so.status not in ('On Hold', 'Closed'))) ) tab where so_item_qty >= so_item_delivered_qty From 1535c3d856a50c31ed1e2adaf022ca1a221aab9b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 17:54:51 +0530 Subject: [PATCH 05/36] fix: asset_depreciation_and_balances report doesn't reflect manual depr entries --- .../asset_depreciations_and_balances.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index ad9b1ba58eb..9cea37c4f80 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -135,6 +135,34 @@ def get_assets(filters): where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union + SELECT a.asset_category, + ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then + gle.debit + else + 0 + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then + gle.debit + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s + and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then + gle.debit + else + 0 + end), 0) as depreciation_amount_during_the_period + from `tabGL Entry` gle + join `tabAsset` a on + gle.against_voucher = a.name + join `tabAsset Category Account` aca on + aca.parent = a.asset_category and aca.company_name = %(company)s + join `tabCompany` company on + company.name = %(company)s + where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + group by a.asset_category + union SELECT a.asset_category, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 From dbd446681791bde00ca8ea4d3f71072f915e885b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 18:33:01 +0530 Subject: [PATCH 06/36] chore: add anand to asset's codeowner (cherry picked from commit d003370f61ef405bd69932005a1c02d35fe947ee) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index e0a0fb75f8e..1657fdacc96 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,7 +4,7 @@ # the repo. Unless a later match takes precedence, erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar -erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar +erpnext/assets/ @anandbaburajan @deepeshgarg007 erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007 erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar From a7b682e26b2e1c810891effc3e15606573fb1168 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 14 Feb 2023 19:03:07 +0530 Subject: [PATCH 07/36] feat: allow to make in transit transfer entry from material request (cherry picked from commit 5b6128848f0c9b9fb834a3f17eedf99cb2b4fd96) --- .../material_request/material_request.js | 45 +++++++++++++++++- .../material_request/material_request.py | 11 +++++ .../material_request/test_material_request.py | 47 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 156e5917f23..c1f1b0d1352 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -110,8 +110,11 @@ frappe.ui.form.on('Material Request', { if (frm.doc.material_request_type === "Material Transfer") { add_create_pick_list_button(); - frm.add_custom_button(__("Transfer Material"), + frm.add_custom_button(__("Material Transfer"), () => frm.events.make_stock_entry(frm), __('Create')); + + frm.add_custom_button(__("Material Transfer (In Transit)"), + () => frm.events.make_in_transit_stock_entry(frm), __('Create')); } if (frm.doc.material_request_type === "Material Issue") { @@ -333,6 +336,46 @@ frappe.ui.form.on('Material Request', { }); }, + make_in_transit_stock_entry(frm) { + frappe.prompt( + [ + { + label: __('In Transit Warehouse'), + fieldname: 'in_transit_warehouse', + fieldtype: 'Link', + options: 'Warehouse', + reqd: 1, + get_query: () => { + return{ + filters: { + 'company': frm.doc.company, + 'is_group': 0, + 'warehouse_type': 'Transit' + } + } + } + } + ], + (values) => { + frappe.call({ + method: "erpnext.stock.doctype.material_request.material_request.make_in_transit_stock_entry", + args: { + source_name: frm.doc.name, + in_transit_warehouse: values.in_transit_warehouse + }, + callback: function(r) { + if (r.message) { + let doc = frappe.model.sync(r.message); + frappe.set_route('Form', doc[0].doctype, doc[0].name); + } + } + }) + }, + __('In Transit Transfer'), + __("Create Stock Entry") + ) + }, + create_pick_list: (frm) => { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.material_request.material_request.create_pick_list", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index afad7511be6..1fe2ee5504c 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -719,3 +719,14 @@ def create_pick_list(source_name, target_doc=None): doc.set_item_locations() return doc + + +@frappe.whitelist() +def make_in_transit_stock_entry(source_name, in_transit_warehouse): + ste_doc = make_stock_entry(source_name) + ste_doc.add_to_transit = 1 + + for row in ste_doc.items: + row.t_warehouse = in_transit_warehouse + + return ste_doc diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index f0a94997fe8..a707c74c7db 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -11,6 +11,7 @@ from frappe.utils import flt, today from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.material_request.material_request import ( + make_in_transit_stock_entry, make_purchase_order, make_stock_entry, make_supplier_quotation, @@ -56,6 +57,22 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(se.doctype, "Stock Entry") self.assertEqual(len(se.get("items")), len(mr.get("items"))) + def test_in_transit_make_stock_entry(self): + mr = frappe.copy_doc(test_records[0]).insert() + + self.assertRaises(frappe.ValidationError, make_stock_entry, mr.name) + + mr = frappe.get_doc("Material Request", mr.name) + mr.material_request_type = "Material Transfer" + mr.submit() + + in_transit_warehouse = get_in_transit_warehouse(mr.company) + se = make_in_transit_stock_entry(mr.name, in_transit_warehouse) + + self.assertEqual(se.doctype, "Stock Entry") + for row in se.get("items"): + self.assertEqual(row.t_warehouse, in_transit_warehouse) + def _insert_stock_entry(self, qty1, qty2, warehouse=None): se = frappe.get_doc( { @@ -742,6 +759,36 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(existing_requested_qty, current_requested_qty) +def get_in_transit_warehouse(company): + if not frappe.db.exists("Warehouse Type", "Transit"): + frappe.get_doc( + { + "doctype": "Warehouse Type", + "name": "Transit", + } + ).insert() + + in_transit_warehouse = frappe.db.exists( + "Warehouse", {"warehouse_type": "Transit", "company": company} + ) + + if not in_transit_warehouse: + in_transit_warehouse = ( + frappe.get_doc( + { + "doctype": "Warehouse", + "warehouse_name": "Transit", + "warehouse_type": "Transit", + "company": company, + } + ) + .insert() + .name + ) + + return in_transit_warehouse + + def make_material_request(**args): args = frappe._dict(args) mr = frappe.new_doc("Material Request") From f65e471a75566efbc0ea46bef94b19644ecd767c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 15 Feb 2023 09:28:56 +0530 Subject: [PATCH 08/36] fix: linters issue --- .../stock/doctype/material_request/material_request.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index c1f1b0d1352..b096b024f44 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -108,12 +108,12 @@ frappe.ui.form.on('Material Request', { () => frm.events.create_pick_list(frm), __('Create')); } - if (frm.doc.material_request_type === "Material Transfer") { + if (frm.doc.material_request_type === 'Material Transfer') { add_create_pick_list_button(); - frm.add_custom_button(__("Material Transfer"), + frm.add_custom_button(__('Material Transfer'), () => frm.events.make_stock_entry(frm), __('Create')); - frm.add_custom_button(__("Material Transfer (In Transit)"), + frm.add_custom_button(__('Material Transfer (In Transit)'), () => frm.events.make_in_transit_stock_entry(frm), __('Create')); } @@ -372,7 +372,7 @@ frappe.ui.form.on('Material Request', { }) }, __('In Transit Transfer'), - __("Create Stock Entry") + __('Create Stock Entry') ) }, From 4f37ba9cfedf52973b0f91e7fc27ced7d556e9dc Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 14 Feb 2023 19:34:36 +0100 Subject: [PATCH 09/36] fix: change parameter name for letter head To match changes from https://github.com/frappe/frappe/pull/19627 (cherry picked from commit f7fd30fecfb2cb570cf212d011dee5f1fcb13cf0) --- .../doctype/request_for_quotation/request_for_quotation.js | 2 +- .../doctype/request_for_quotation/request_for_quotation.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index a9f5afb2e98..7f8dd56f51d 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -129,7 +129,7 @@ frappe.ui.form.on("Request for Quotation",{ supplier: data.supplier, print_format: data.print_format || "Standard", language: data.language || frappe.boot.lang, - letter_head: data.letter_head || frm.doc.letter_head || "", + letterhead: data.letter_head || frm.doc.letter_head || "", }).toString() ) ); diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 8e9ded98421..332705e9339 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -388,7 +388,7 @@ def create_rfq_items(sq_doc, supplier, data): @frappe.whitelist() -def get_pdf(doctype, name, supplier, print_format=None, language=None, letter_head=None): +def get_pdf(doctype, name, supplier, print_format=None, language=None, letterhead=None): # permissions get checked in `download_pdf` if doc := get_rfq_doc(doctype, name, supplier): download_pdf( @@ -397,7 +397,7 @@ def get_pdf(doctype, name, supplier, print_format=None, language=None, letter_he print_format, doc=doc, language=language, - letter_head=letter_head or None, + letterhead=letterhead or None, ) From 1e2611fb1edac4cf6d9009674f5ad8400ef1705f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 14 Feb 2023 19:53:03 +0100 Subject: [PATCH 10/36] refactor: download RFQ PDF (cherry picked from commit 8e40c04494930c20b37bc48212639326e8f3857f) --- .../request_for_quotation.js | 1 - .../request_for_quotation.py | 35 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 7f8dd56f51d..2f0b7862a82 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -124,7 +124,6 @@ frappe.ui.form.on("Request for Quotation",{ frappe.urllib.get_full_url( "/api/method/erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_pdf?" + new URLSearchParams({ - doctype: frm.doc.doctype, name: frm.doc.name, supplier: data.supplier, print_format: data.print_format || "Standard", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 332705e9339..7927beb8233 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -3,6 +3,7 @@ import json +from typing import Optional import frappe from frappe import _ @@ -388,24 +389,26 @@ def create_rfq_items(sq_doc, supplier, data): @frappe.whitelist() -def get_pdf(doctype, name, supplier, print_format=None, language=None, letterhead=None): - # permissions get checked in `download_pdf` - if doc := get_rfq_doc(doctype, name, supplier): - download_pdf( - doctype, - name, - print_format, - doc=doc, - language=language, - letterhead=letterhead or None, - ) - - -def get_rfq_doc(doctype, name, supplier): +def get_pdf( + name: str, + supplier: str, + print_format: Optional[str] = None, + language: Optional[str] = None, + letterhead: Optional[str] = None, +): + doc = frappe.get_doc("Request for Quotation", name) if supplier: - doc = frappe.get_doc(doctype, name) doc.update_supplier_part_no(supplier) - return doc + + # permissions get checked in `download_pdf` + download_pdf( + doc.doctype, + doc.name, + print_format, + doc=doc, + language=language, + letterhead=letterhead or None, + ) @frappe.whitelist() From bb174f931db4fea93984f6083a2d4de15030f150 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 14 Feb 2023 19:53:39 +0100 Subject: [PATCH 11/36] test: download RFQ PDF (cherry picked from commit d5b7f2e49e5d8c88d72815ff702823a86f2587de) --- .../request_for_quotation/test_request_for_quotation.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index 064b806e953..d250e6f18a9 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -8,6 +8,7 @@ from frappe.utils import nowdate from erpnext.buying.doctype.request_for_quotation.request_for_quotation import ( create_supplier_quotation, + get_pdf, make_supplier_quotation_from_rfq, ) from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq @@ -124,6 +125,11 @@ class TestRequestforQuotation(FrappeTestCase): rfq.status = "Draft" rfq.submit() + def test_get_pdf(self): + rfq = make_request_for_quotation() + get_pdf(rfq.name, rfq.get("suppliers")[0].supplier) + self.assertEqual(frappe.local.response.type, "pdf") + def make_request_for_quotation(**args): """ From 72e17e32678333ff28f88ea749a512b0b85e1fc3 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 9 Feb 2023 16:15:07 +0530 Subject: [PATCH 12/36] chore: copy `item_code` to `Batch` while creating new batch from SCR (cherry picked from commit ffa9c6e4d9f417de8bd0631d23c00fa2ac772ba2) --- .../subcontracting_receipt.js | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index b6bef8c4a02..3a2c53f4e44 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -51,13 +51,31 @@ frappe.ui.form.on('Subcontracting Receipt', { } })); - frm.set_query("expense_account", "items", function () { + frm.set_query('expense_account', 'items', function () { return { - query: "erpnext.controllers.queries.get_expense_account", + query: 'erpnext.controllers.queries.get_expense_account', filters: { 'company': frm.doc.company } }; }); + frm.set_query('batch_no', 'items', function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + return { + filters: { + item: row.item_code + } + } + }); + + let batch_no_field = frm.get_docfield("items", "batch_no"); + if (batch_no_field) { + batch_no_field.get_route_options_for_new_doc = function(row) { + return { + "item": row.doc.item_code + } + }; + } + frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => { if (val == 'Material Transferred for Subcontract') { frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => { @@ -73,7 +91,7 @@ frappe.ui.form.on('Subcontracting Receipt', { refresh: (frm) => { if (frm.doc.docstatus > 0) { - frm.add_custom_button(__("Stock Ledger"), function () { + frm.add_custom_button(__('Stock Ledger'), function () { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, @@ -81,8 +99,8 @@ frappe.ui.form.on('Subcontracting Receipt', { company: frm.doc.company, show_cancelled_entries: frm.doc.docstatus === 2 }; - frappe.set_route("query-report", "Stock Ledger"); - }, __("View")); + frappe.set_route('query-report', 'Stock Ledger'); + }, __('View')); frm.add_custom_button(__('Accounting Ledger'), function () { frappe.route_options = { @@ -90,11 +108,11 @@ frappe.ui.form.on('Subcontracting Receipt', { from_date: frm.doc.posting_date, to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by: "Group by Voucher (Consolidated)", + group_by: 'Group by Voucher (Consolidated)', show_cancelled_entries: frm.doc.docstatus === 2 }; - frappe.set_route("query-report", "General Ledger"); - }, __("View")); + frappe.set_route('query-report', 'General Ledger'); + }, __('View')); } if (!frm.doc.is_return && frm.doc.docstatus == 1 && frm.doc.per_returned < 100) { @@ -111,25 +129,25 @@ frappe.ui.form.on('Subcontracting Receipt', { frm.add_custom_button(__('Subcontracting Order'), function () { if (!frm.doc.supplier) { frappe.throw({ - title: __("Mandatory"), - message: __("Please Select a Supplier") + title: __('Mandatory'), + message: __('Please Select a Supplier') }); } erpnext.utils.map_current_doc({ method: 'erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.make_subcontracting_receipt', - source_doctype: "Subcontracting Order", + source_doctype: 'Subcontracting Order', target: frm, setters: { supplier: frm.doc.supplier, }, get_query_filters: { docstatus: 1, - per_received: ["<", 100], + per_received: ['<', 100], company: frm.doc.company } }); - }, __("Get Items From")); + }, __('Get Items From')); } }, From ba5ea886cf59bf0c490b9401813649cc1107ca93 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 13 Feb 2023 22:37:26 +0530 Subject: [PATCH 13/36] fix: create `Delivery Trip` from `Delivery Note` list (cherry picked from commit 6c6195bae6e5fb315ba38d346da11b13ba18990d) --- .../doctype/delivery_note/delivery_note_list.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index 9e6f3bc9321..6ff3ed3e8e5 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -14,7 +14,7 @@ frappe.listview_settings['Delivery Note'] = { return [__("Completed"), "green", "per_billed,=,100"]; } }, - onload: function (listview) { + onload: function (doclist) { const action = () => { const selected_docs = doclist.get_checked_items(); const docnames = doclist.get_checked_items(true); @@ -56,14 +56,14 @@ frappe.listview_settings['Delivery Note'] = { // doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false); - listview.page.add_action_item(__('Create Delivery Trip'), action); + doclist.page.add_action_item(__('Create Delivery Trip'), action); - listview.page.add_action_item(__("Sales Invoice"), ()=>{ - erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Sales Invoice"); + doclist.page.add_action_item(__("Sales Invoice"), ()=>{ + erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Sales Invoice"); }); - listview.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{ - erpnext.bulk_transaction_processing.create(listview, "Delivery Note", "Packing Slip"); + doclist.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{ + erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Packing Slip"); }); } }; From d7831685afcbb4a4fe15cee7abba113f43a996d1 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Thu, 16 Feb 2023 09:16:19 +0100 Subject: [PATCH 14/36] fix: rename duplicate field name with same type into a DocType to avoid import Error (#34053) * fix: Delivery Note field label set * fix: Item field label de-duplicate name * fix: Payment Entry field label de-duplicate name * fix: Pruicing Rule field label de-duplicate name * fix: Project field label de-duplicate name * fix: Timesheet field label de-duplicate name * Update erpnext/accounts/doctype/pricing_rule/pricing_rule.json Co-authored-by: Sagar Sharma * Update erpnext/stock/doctype/item/item.json Co-authored-by: Sagar Sharma --------- Co-authored-by: Sagar Sharma (cherry picked from commit 4d79ffe3c6eff810e0244479aec1dc3ba650f10d) # Conflicts: # erpnext/stock/doctype/item/item.json --- .../doctype/payment_entry/payment_entry.json | 18 +++++++++--------- .../doctype/pricing_rule/pricing_rule.json | 4 ++-- erpnext/projects/doctype/project/project.json | 8 ++++---- .../projects/doctype/timesheet/timesheet.json | 10 ++++++---- .../doctype/delivery_note/delivery_note.json | 4 +++- erpnext/stock/doctype/item/item.json | 6 +++++- 6 files changed, 29 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 4a7a57b6275..3927ecae43d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -239,7 +239,7 @@ "depends_on": "paid_from", "fieldname": "paid_from_account_currency", "fieldtype": "Link", - "label": "Account Currency", + "label": "Account Currency (From)", "options": "Currency", "print_hide": 1, "read_only": 1, @@ -249,7 +249,7 @@ "depends_on": "paid_from", "fieldname": "paid_from_account_balance", "fieldtype": "Currency", - "label": "Account Balance", + "label": "Account Balance (From)", "options": "paid_from_account_currency", "print_hide": 1, "read_only": 1 @@ -272,7 +272,7 @@ "depends_on": "paid_to", "fieldname": "paid_to_account_currency", "fieldtype": "Link", - "label": "Account Currency", + "label": "Account Currency (To)", "options": "Currency", "print_hide": 1, "read_only": 1, @@ -282,7 +282,7 @@ "depends_on": "paid_to", "fieldname": "paid_to_account_balance", "fieldtype": "Currency", - "label": "Account Balance", + "label": "Account Balance (To)", "options": "paid_to_account_currency", "print_hide": 1, "read_only": 1 @@ -304,7 +304,7 @@ { "fieldname": "source_exchange_rate", "fieldtype": "Float", - "label": "Exchange Rate", + "label": "Source Exchange Rate", "precision": "9", "print_hide": 1, "reqd": 1 @@ -334,7 +334,7 @@ { "fieldname": "target_exchange_rate", "fieldtype": "Float", - "label": "Exchange Rate", + "label": "Target Exchange Rate", "precision": "9", "print_hide": 1, "reqd": 1 @@ -633,14 +633,14 @@ "depends_on": "eval:doc.party_type == 'Supplier'", "fieldname": "purchase_taxes_and_charges_template", "fieldtype": "Link", - "label": "Taxes and Charges Template", + "label": "Purchase Taxes and Charges Template", "options": "Purchase Taxes and Charges Template" }, { "depends_on": "eval: doc.party_type == 'Customer'", "fieldname": "sales_taxes_and_charges_template", "fieldtype": "Link", - "label": "Taxes and Charges Template", + "label": "Sales Taxes and Charges Template", "options": "Sales Taxes and Charges Template" }, { @@ -733,7 +733,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-12-08 16:25:43.824051", + "modified": "2023-02-14 04:52:30.478523", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index ce9ce647db0..a63039e0e3a 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -472,7 +472,7 @@ "description": "If rate is zero them item will be treated as \"Free Item\"", "fieldname": "free_item_rate", "fieldtype": "Currency", - "label": "Rate" + "label": "Free Item Rate" }, { "collapsible": 1, @@ -608,7 +608,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2022-10-13 19:05:35.056304", + "modified": "2023-02-14 04:53:34.887358", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 37d98ad8ea1..ba7aa850825 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -408,7 +408,7 @@ "depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)", "fieldname": "daily_time_to_send", "fieldtype": "Time", - "label": "Time to send" + "label": "Daily Time to send" }, { "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", @@ -421,7 +421,7 @@ "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", "fieldname": "weekly_time_to_send", "fieldtype": "Time", - "label": "Time to send" + "label": "Weekly Time to send" }, { "fieldname": "column_break_45", @@ -451,7 +451,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 4, - "modified": "2022-06-23 16:45:06.108499", + "modified": "2023-02-14 04:54:25.819620", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -497,4 +497,4 @@ "timeline_field": "customer", "title_field": "project_name", "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 0cce129034e..468300661a0 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -282,21 +282,21 @@ { "fieldname": "base_total_costing_amount", "fieldtype": "Currency", - "label": "Total Costing Amount", + "label": "Base Total Costing Amount", "print_hide": 1, "read_only": 1 }, { "fieldname": "base_total_billable_amount", "fieldtype": "Currency", - "label": "Total Billable Amount", + "label": "Base Total Billable Amount", "print_hide": 1, "read_only": 1 }, { "fieldname": "base_total_billed_amount", "fieldtype": "Currency", - "label": "Total Billed Amount", + "label": "Base Total Billed Amount", "print_hide": 1, "read_only": 1 }, @@ -311,10 +311,11 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-15 22:08:53.930200", + "modified": "2023-02-14 04:55:41.735991", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -388,5 +389,6 @@ ], "sort_field": "modified", "sort_order": "ASC", + "states": [], "title_field": "title" } \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 165a56b7839..0c1f82029e6 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -521,6 +521,7 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", + "label": "Delivery Note Item", "oldfieldname": "delivery_note_details", "oldfieldtype": "Table", "options": "Delivery Note Item", @@ -666,6 +667,7 @@ { "fieldname": "taxes", "fieldtype": "Table", + "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -1401,7 +1403,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-12-12 18:38:53.067799", + "modified": "2023-02-14 04:45:44.179670", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index d1d228dfdc6..aef8dde7733 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -706,7 +706,7 @@ "depends_on": "enable_deferred_expense", "fieldname": "no_of_months_exp", "fieldtype": "Int", - "label": "No of Months" + "label": "No of Months (Expense)" }, { "collapsible": 1, @@ -911,7 +911,11 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, +<<<<<<< HEAD "modified": "2022-09-13 04:08:17.431731", +======= + "modified": "2023-02-14 04:48:26.343620", +>>>>>>> 4d79ffe3c6 (fix: rename duplicate field name with same type into a DocType to avoid import Error (#34053)) "modified_by": "Administrator", "module": "Stock", "name": "Item", From 2fdf6ab16497e11e33b2afa4865d5c5d828ce6f1 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 16 Feb 2023 14:01:17 +0530 Subject: [PATCH 15/36] chore: `conflicts` --- erpnext/stock/doctype/item/item.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index aef8dde7733..e519b9b2133 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -911,11 +911,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, -<<<<<<< HEAD - "modified": "2022-09-13 04:08:17.431731", -======= "modified": "2023-02-14 04:48:26.343620", ->>>>>>> 4d79ffe3c6 (fix: rename duplicate field name with same type into a DocType to avoid import Error (#34053)) "modified_by": "Administrator", "module": "Stock", "name": "Item", From f7ea98cf44aeee534ded0a44819d46718725f67b Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 16 Feb 2023 12:55:37 +0530 Subject: [PATCH 16/36] chore: update `CODEOWNERS` (cherry picked from commit e8b8c51d823775aa7ed0230b565ce0001758db7b) --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 1657fdacc96..465337a0c5c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -16,6 +16,7 @@ erpnext/maintenance/ @rohitwaghchaure @s-aga-r erpnext/manufacturing/ @rohitwaghchaure @s-aga-r erpnext/quality_management/ @rohitwaghchaure @s-aga-r erpnext/stock/ @rohitwaghchaure @s-aga-r +erpnext/subcontracting @rohitwaghchaure @s-aga-r erpnext/crm/ @NagariaHussain erpnext/education/ @rutwikhdev From d5f6a5d193d538550101f59b6b035de8024e8da7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 16 Feb 2023 13:21:17 +0530 Subject: [PATCH 17/36] fix: incorrect consumed qty in subcontracting receipt (cherry picked from commit 156e45970a5b1dbea37f687bfbcf8607b4d5cc8b) --- .../controllers/subcontracting_controller.py | 9 ++++- .../test_subcontracting_order.py | 35 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index a9561fe2dac..cc80f6ca984 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -409,7 +409,14 @@ class SubcontractingController(StockController): if self.available_materials.get(key) and self.available_materials[key]["batch_no"]: new_rm_obj = None for batch_no, batch_qty in self.available_materials[key]["batch_no"].items(): - if batch_qty >= qty: + if batch_qty >= qty or ( + rm_obj.consumed_qty == 0 + and self.backflush_based_on == "BOM" + and len(self.available_materials[key]["batch_no"]) == 1 + ): + if rm_obj.consumed_qty == 0: + self.__set_consumed_qty(rm_obj, qty) + self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) self.available_materials[key]["batch_no"][batch_no] -= qty return diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index d054ce0f9d4..6a2983faaaf 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -2,6 +2,7 @@ # See license.txt import copy +from collections import defaultdict import frappe from frappe.tests.utils import FrappeTestCase @@ -186,6 +187,40 @@ class TestSubcontractingOrder(FrappeTestCase): ) self.assertEqual(len(ste.items), len(rm_items)) + def test_make_rm_stock_entry_for_batch_items_with_less_transfer(self): + set_backflush_based_on("BOM") + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 4", + "qty": 5, + "rate": 100, + "fg_item": "Subcontracted Item SA4", + "fg_item_qty": 5, + } + ] + + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + + itemwise_transfer_qty = defaultdict(int) + for item in rm_items: + item["qty"] -= 1 + itemwise_transfer_qty[item["item_code"]] += item["qty"] + + ste = make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr = make_subcontracting_receipt(sco.name) + + for row in scr.supplied_items: + self.assertEqual(row.consumed_qty, itemwise_transfer_qty.get(row.rm_item_code) + 1) + def test_update_reserved_qty_for_subcontracting(self): # Create RM Material Receipt make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item", qty=10, basic_rate=100) From 03f07a20e7ae042f840351728f7625525a8b3ef2 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 17 Feb 2023 15:32:55 +0530 Subject: [PATCH 18/36] fix: asset repair status after deletion and asset status after manual depr entry --- .../doctype/journal_entry/journal_entry.py | 22 ++++++------------- erpnext/assets/doctype/asset/asset.js | 2 +- erpnext/assets/doctype/asset/asset.py | 13 +++++++---- erpnext/assets/doctype/asset/depreciation.py | 2 +- .../doctype/asset_repair/asset_repair.py | 3 +++ 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index e52fd6225fb..f15ef52a761 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -238,21 +238,16 @@ class JournalEntry(AccountsController): ): processed_assets.append(d.reference_name) - asset = frappe.db.get_value( - "Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1 - ) + asset = frappe.get_doc("Asset", d.reference_name) if asset.calculate_depreciation: continue depr_value = d.debit or d.credit - frappe.db.set_value( - "Asset", - d.reference_name, - "value_after_depreciation", - asset.value_after_depreciation - depr_value, - ) + asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value) + + asset.set_status() def update_inter_company_jv(self): if ( @@ -341,12 +336,9 @@ class JournalEntry(AccountsController): else: depr_value = d.debit or d.credit - frappe.db.set_value( - "Asset", - d.reference_name, - "value_after_depreciation", - asset.value_after_depreciation + depr_value, - ) + asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value) + + asset.set_status() def unlink_inter_company_jv(self): if ( diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index bffe1995457..1abcf6a55b6 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -250,7 +250,7 @@ frappe.ui.form.on('Asset', { $.each(depr_entries || [], function(i, v) { x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); let last_asset_value = asset_values[asset_values.length - 1] - asset_values.push(last_asset_value - v.value); + asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount'))); }); } diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d3eb958fa6d..711ccc5e050 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -677,11 +677,15 @@ class Asset(AccountsController): if self.journal_entry_for_scrap: status = "Scrapped" - elif self.finance_books: - idx = self.get_default_finance_book_idx() or 0 + else: + expected_value_after_useful_life = 0 + value_after_depreciation = self.value_after_depreciation - expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life - value_after_depreciation = self.finance_books[idx].value_after_depreciation + if self.calculate_depreciation: + idx = self.get_default_finance_book_idx() or 0 + + expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life + value_after_depreciation = self.finance_books[idx].value_after_depreciation if flt(value_after_depreciation) <= expected_value_after_useful_life: status = "Fully Depreciated" @@ -843,6 +847,7 @@ class Asset(AccountsController): .where(gle.debit != 0) .where(gle.is_cancelled == 0) .orderby(gle.posting_date) + .orderby(gle.creation) ).run(as_dict=True) return records diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 196288e9e67..e72e0afb9ce 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -137,7 +137,7 @@ def make_depreciation_entry(asset_name, date=None): finance_books.value_after_depreciation -= d.depreciation_amount finance_books.db_update() - frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful") + asset.db_set("depr_entry_posting_status", "Successful") asset.set_status() diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index d5913c59463..9ccf778a4bb 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -77,6 +77,9 @@ class AssetRepair(AccountsController): self.asset_doc.prepare_depreciation_data() self.asset_doc.save() + def after_delete(self): + frappe.get_doc("Asset", self.asset).set_status() + def check_repair_status(self): if self.repair_status == "Pending": frappe.throw(_("Please update Repair Status.")) From 05f7fb8fa1e6a121d0cb3dc33b9c5d9e7ae454d9 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 7 Feb 2023 14:43:10 +0530 Subject: [PATCH 19/36] chore: add field `show_pay_button` in Buying Settings (cherry picked from commit d7ef5ad95573a2616191811edf09242a7535fc3d) --- .../buying/doctype/buying_settings/buying_settings.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 34417f7ac3a..652dcf0d43c 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -21,6 +21,7 @@ "allow_multiple_items", "bill_for_rejected_quantity_in_purchase_invoice", "disable_last_purchase_rate", + "show_pay_button", "subcontract", "backflush_raw_materials_of_subcontract_based_on", "column_break_11", @@ -140,6 +141,12 @@ "fieldname": "disable_last_purchase_rate", "fieldtype": "Check", "label": "Disable Last Purchase Rate" + }, + { + "default": "1", + "fieldname": "show_pay_button", + "fieldtype": "Check", + "label": "Show Pay Button in Purchase Order Portal" } ], "icon": "fa fa-cog", @@ -147,7 +154,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-01-09 17:08:28.828173", + "modified": "2023-02-15 14:42:10.200679", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", From 84da0c6f1eb8c66d8362ad07079f083aebaf49a9 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 8 Feb 2023 15:02:39 +0530 Subject: [PATCH 20/36] fix: show Purchase Order Portal `Pay` button based on configuration (cherry picked from commit 20bdc63b037187a6375f3d2958633270b82b5e2d) --- erpnext/templates/pages/order.html | 20 +++++++++++--------- erpnext/templates/pages/order.py | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html index 6b354b2fab6..bc34ad5ac50 100644 --- a/erpnext/templates/pages/order.html +++ b/erpnext/templates/pages/order.html @@ -34,16 +34,18 @@ - {% endblock %} diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py index 185ec6615f6..13772d31295 100644 --- a/erpnext/templates/pages/order.py +++ b/erpnext/templates/pages/order.py @@ -55,6 +55,7 @@ def get_context(context): ) context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points")) + context.show_pay_button = frappe.db.get_single_value("Buying Settings", "show_pay_button") context.show_make_pi_button = False if context.doc.get("supplier"): # show Make Purchase Invoice button based on permission From 72c0b2208fdea3e4660858575c9af17b52338547 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Feb 2023 20:46:09 +0530 Subject: [PATCH 21/36] feat: provision to convert transaction based reposting to item warehouse based reposting (cherry picked from commit f1383b5ef91a536dc13d2b3638823597a9954f14) --- .../stock_reposting_settings.js | 19 +++++- .../stock_reposting_settings.py | 61 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js index 42d0723d427..5f81679bade 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js @@ -2,7 +2,22 @@ // For license information, please see license.txt frappe.ui.form.on('Stock Reposting Settings', { - // refresh: function(frm) { + refresh: function(frm) { + frm.trigger('convert_to_item_based_reposting'); + }, - // } + convert_to_item_based_reposting: function(frm) { + frm.add_custom_button(__('Convert to Item Based Reposting'), function() { + frm.call({ + method: 'convert_to_item_wh_reposting', + frezz: true, + doc: frm.doc, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + } + } + }) + }) + } }); diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py index e0c8ed12e7d..51fb5ac4c40 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py @@ -1,6 +1,8 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import frappe +from frappe import _ from frappe.model.document import Document from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours @@ -24,3 +26,62 @@ class StockRepostingSettings(Document): if diff < 10: self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True)) + + @frappe.whitelist() + def convert_to_item_wh_reposting(self): + """Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled.""" + + reposting_data = get_reposting_entries() + + vouchers = [d.voucher_no for d in reposting_data] + + item_warehouses = {} + + for ledger in get_stock_ledgers(vouchers): + key = (ledger.item_code, ledger.warehouse) + if key not in item_warehouses: + item_warehouses[key] = ledger.posting_date + elif frappe.utils.getdate(item_warehouses.get(key)) > frappe.utils.getdate(ledger.posting_date): + item_warehouses[key] = ledger.posting_date + + for key, posting_date in item_warehouses.items(): + item_code, warehouse = key + create_repost_item_valuation(item_code, warehouse, posting_date) + + for row in reposting_data: + frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped") + + self.db_set("item_based_reposting", 1) + frappe.msgprint(_("Item Warehouse based reposting has been enabled.")) + + +def get_reposting_entries(): + return frappe.get_all( + "Repost Item Valuation", + fields=["voucher_no", "name"], + filters={"status": ("in", ["Queued", "In Progress"]), "docstatus": 1, "based_on": "Transaction"}, + ) + + +def get_stock_ledgers(vouchers): + return frappe.get_all( + "Stock Ledger Entry", + fields=["item_code", "warehouse", "posting_date"], + filters={"voucher_no": ("in", vouchers)}, + ) + + +def create_repost_item_valuation(item_code, warehouse, posting_date): + frappe.get_doc( + { + "doctype": "Repost Item Valuation", + "company": frappe.get_cached_value("Warehouse", warehouse, "company"), + "posting_date": posting_date, + "based_on": "Item and Warehouse", + "posting_time": "00:00:01", + "item_code": item_code, + "warehouse": warehouse, + "allow_negative_stock": True, + "status": "Queued", + } + ).submit() From 53ab4d92e80a6b8b319887a6555e18da984a5a1e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 12:08:12 +0530 Subject: [PATCH 22/36] fix: forced delete linked desktop_icons (backport #34107) (#34130) fix: forced delete linked desktop_icons (#34107) * fix: forced delete linked desktop_icons * fix: black (cherry picked from commit 5e48e61c66558d43aa9a45eba8cfe90182c360f1) Co-authored-by: Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com> --- erpnext/patches/v13_0/delete_old_purchase_reports.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py index 987f53f37c1..60621fbc9ca 100644 --- a/erpnext/patches/v13_0/delete_old_purchase_reports.py +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -17,10 +17,11 @@ def execute(): for report in reports_to_delete: if frappe.db.exists("Report", report): + delete_links_from_desktop_icons(report) delete_auto_email_reports(report) check_and_delete_linked_reports(report) - frappe.delete_doc("Report", report) + frappe.delete_doc("Report", report, force=True) def delete_auto_email_reports(report): @@ -28,3 +29,10 @@ def delete_auto_email_reports(report): auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) for auto_email_report in auto_email_reports: frappe.delete_doc("Auto Email Report", auto_email_report[0]) + + +def delete_links_from_desktop_icons(report): + """Check for one or multiple Desktop Icons and delete""" + desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"]) + for desktop_icon in desktop_icons: + frappe.delete_doc("Desktop Icon", desktop_icon[0], force=True) From 78795643cc13fe544cab03eb3989bd7ee8f31f07 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 13:38:02 +0530 Subject: [PATCH 23/36] fix: consider rounded total amount while making payment request (#34110) fix: consider rounded total amount while making payment request (#34110) (cherry picked from commit 9c6466f15b90b9100635b76356f0a68426b053b1) Co-authored-by: ruthra kumar --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 86c373cf93d..b0ee5885482 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -495,7 +495,7 @@ def get_amount(ref_doc, payment_account=None): """get amount based on doctype""" dt = ref_doc.doctype if dt in ["Sales Order", "Purchase Order"]: - grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) + grand_total = flt(ref_doc.rounded_total) - flt(ref_doc.advance_paid) elif dt in ["Sales Invoice", "Purchase Invoice"]: if ref_doc.party_account_currency == ref_doc.currency: From 9890cce6808820a3d149bc3ac93543091fecbdb3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 20:50:37 +0530 Subject: [PATCH 24/36] fix: ignore repost payment ledger on basic documents cancellation (#34054) fix: ignore repost payment ledger on basic documents cancellation (#34054) fix: ignore repost payment ledger on cancel/delete of Inv/Pay/JE's Co-authored-by: Deepesh Garg (cherry picked from commit c722f2819c20fc7d35692bfc545897a666322abd) Co-authored-by: ruthra kumar --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 2 +- erpnext/accounts/doctype/journal_entry/journal_entry.py | 8 +++++++- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- erpnext/accounts/doctype/payment_entry/payment_entry.py | 8 +++++++- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 2 ++ erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 ++ erpnext/controllers/accounts_controller.py | 6 ++++++ 9 files changed, 28 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 21f27aedc51..089f20b467d 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry"); frappe.ui.form.on("Journal Entry", { setup: function(frm) { frm.add_fetch("bank_account", "account", "account"); - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry']; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger"]; }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index f15ef52a761..56fe333e047 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -89,7 +89,13 @@ class JournalEntry(AccountsController): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries unlink_ref_doc_from_payment_entries(self) - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Stock Ledger Entry", + "Payment Ledger Entry", + "Repost Payment Ledger", + "Repost Payment Ledger Items", + ) self.make_gl_entries(1) self.update_advance_paid() self.unlink_advance_entry_reference() diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 6039bdfe95f..91374ae217b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges"; frappe.ui.form.on('Payment Entry', { onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice']; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"]; if(frm.doc.__islocal) { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index bc2a1e50793..a585924d20f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -92,7 +92,13 @@ class PaymentEntry(AccountsController): self.set_status() def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Stock Ledger Entry", + "Payment Ledger Entry", + "Repost Payment Ledger", + "Repost Payment Ledger Items", + ) self.make_gl_entries(cancel=1) self.update_outstanding_amounts() self.update_advance_paid() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index a098e8d1db8..e2b4a1ad5be 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice']; + this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger"]; if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index df4c2770e3c..81824e11a2d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1416,6 +1416,8 @@ class PurchaseInvoice(BuyingController): "GL Entry", "Stock Ledger Entry", "Repost Item Valuation", + "Repost Payment Ledger", + "Repost Payment Ledger Items", "Payment Ledger Entry", "Tax Withheld Vouchers", ) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a06da72ccbf..89647da6e10 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e super.onload(); this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', - 'POS Closing Entry', 'Journal Entry', 'Payment Entry']; + 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger"]; if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4748826ec37..a56a2cf56ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -390,6 +390,8 @@ class SalesInvoice(SellingController): "GL Entry", "Stock Ledger Entry", "Repost Item Valuation", + "Repost Payment Ledger", + "Repost Payment Ledger Items", "Payment Ledger Entry", ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6fa44c93c22..3705fcf4990 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -204,6 +204,12 @@ class AccountsController(TransactionBase): validate_einvoice_fields(self) def on_trash(self): + # delete references in 'Repost Payment Ledger' + rpi = frappe.qb.DocType("Repost Payment Ledger Items") + frappe.qb.from_(rpi).delete().where( + (rpi.voucher_type == self.doctype) & (rpi.voucher_no == self.name) + ).run() + # delete sl and gl entries on deletion of transaction if frappe.db.get_single_value("Accounts Settings", "delete_linked_ledger_entries"): ple = frappe.qb.DocType("Payment Ledger Entry") From 75567391a72f91251b721bf1bec2e97833fbd20f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 19 Feb 2023 20:50:56 +0530 Subject: [PATCH 25/36] fix: differency entry journal debit/credit missing (#34104) fix: differency entry journal debit/credit missing (#34104) * fix: difference entry journal is wrong * fix: difference entry journal is wrong (cherry picked from commit 183e42af1a727b3597d0c48daf68b7f3c3d7f1ec) Co-authored-by: Alirio Castro --- .../doctype/payment_reconciliation/payment_reconciliation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 675a3287fa4..e3d9c26b2d1 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -368,6 +368,7 @@ class PaymentReconciliation(Document): "exchange_rate": 1, "cost_center": erpnext.get_default_cost_center(self.company), reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount), + reverse_dr_or_cr: flt(row.difference_amount), } ) From 8d98599a6c634b8ac418bc65196135ab5699829f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Jan 2023 23:07:54 +0530 Subject: [PATCH 26/36] fix: purchase invoice performance issue (cherry picked from commit 92d857d49c151e369451d07d36cf782e2f0e860b) --- .../purchase_invoice/purchase_invoice.py | 30 ++++++++++++---- .../purchase_receipt/purchase_receipt.py | 35 +++++++++++++------ .../purchase_receipt_item.json | 8 +++-- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 81824e11a2d..3bac68a8f65 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -5,6 +5,7 @@ import frappe from frappe import _, throw from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate import erpnext @@ -1465,19 +1466,16 @@ class PurchaseInvoice(BuyingController): def update_billing_status_in_pr(self, update_modified=True): updated_pr = [] po_details = [] + + pr_details_billed_amt = self.get_pr_details_billed_amt() + for d in self.get("items"): if d.pr_detail: - billed_amt = frappe.db.sql( - """select sum(amount) from `tabPurchase Invoice Item` - where pr_detail=%s and docstatus=1""", - d.pr_detail, - ) - billed_amt = billed_amt and billed_amt[0][0] or 0 frappe.db.set_value( "Purchase Receipt Item", d.pr_detail, "billed_amt", - billed_amt, + flt(pr_details_billed_amt.get(d.pr_detail)), update_modified=update_modified, ) updated_pr.append(d.purchase_receipt) @@ -1493,6 +1491,24 @@ class PurchaseInvoice(BuyingController): pr_doc = frappe.get_doc("Purchase Receipt", pr) update_billing_percentage(pr_doc, update_modified=update_modified) + def get_pr_details_billed_amt(self): + # Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice + + pr_details_billed_amt = {} + pr_details = [d.get("pr_detail") for d in self.get("items") if d.get("pr_detail")] + if pr_details: + doctype = frappe.qb.DocType("Purchase Invoice Item") + query = ( + frappe.qb.from_(doctype) + .select(doctype.pr_detail, Sum(doctype.amount)) + .where(doctype.pr_detail.isin(pr_details) & doctype.docstatus == 1) + .groupby(doctype.pr_detail) + ) + + pr_details_billed_amt = frappe._dict(query.run(as_list=1)) + + return pr_details_billed_amt + def on_recurring(self, reference_doc, auto_repeat_doc): self.due_date = None diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 3739cb8c9dd..e6025abf067 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -887,18 +887,10 @@ def update_billing_percentage(pr_doc, update_modified=True): # Update Billing % based on pending accepted qty total_amount, total_billed_amount = 0, 0 - for item in pr_doc.items: - return_data = frappe.db.get_list( - "Purchase Receipt", - fields=["sum(abs(`tabPurchase Receipt Item`.qty)) as qty"], - filters=[ - ["Purchase Receipt", "docstatus", "=", 1], - ["Purchase Receipt", "is_return", "=", 1], - ["Purchase Receipt Item", "purchase_receipt_item", "=", item.name], - ], - ) + item_wise_returned_qty = get_item_wise_returned_qty(pr_doc) - returned_qty = return_data[0].qty if return_data else 0 + for item in pr_doc.items: + returned_qty = flt(item_wise_returned_qty.get(item.name)) returned_amount = flt(returned_qty) * flt(item.rate) pending_amount = flt(item.amount) - returned_amount total_billable_amount = pending_amount if item.billed_amt <= pending_amount else item.billed_amt @@ -915,6 +907,27 @@ def update_billing_percentage(pr_doc, update_modified=True): pr_doc.notify_update() +def get_item_wise_returned_qty(pr_doc): + items = [d.name for d in pr_doc.items] + + return frappe._dict( + frappe.get_all( + "Purchase Receipt", + fields=[ + "`tabPurchase Receipt Item`.purchase_receipt_item", + "sum(abs(`tabPurchase Receipt Item`.qty)) as qty", + ], + filters=[ + ["Purchase Receipt", "docstatus", "=", 1], + ["Purchase Receipt", "is_return", "=", 1], + ["Purchase Receipt Item", "purchase_receipt_item", "in", items], + ], + group_by="`tabPurchase Receipt Item`.purchase_receipt_item", + as_list=1, + ) + ) + + @frappe.whitelist() def make_purchase_invoice(source_name, target_doc=None): from erpnext.accounts.party import get_payment_terms_template diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 557bb594bf0..7a350b9e446 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -859,7 +859,8 @@ "label": "Purchase Receipt Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "collapsible": 1, @@ -974,7 +975,8 @@ "label": "Purchase Invoice Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "product_bundle", @@ -1010,7 +1012,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-11-02 12:49:28.746701", + "modified": "2023-01-18 15:48:58.114923", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 695928389384074764cc902d3fd2ed393210a2d6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 20 Feb 2023 12:20:03 +0530 Subject: [PATCH 27/36] fix: inventory dimension filter not overriding with existing filter for stock ledger report (cherry picked from commit 0e388ba87255ca196068d55afa4839bfc321f56d) --- erpnext/public/js/utils.js | 9 +++++++-- erpnext/stock/report/stock_ledger/stock_ledger.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 51dcd64d9dd..58aa8d7da23 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -221,9 +221,9 @@ $.extend(erpnext.utils, { callback: function(r) { if (r.message && r.message.length) { r.message.forEach((dimension) => { - let found = filters.some(el => el.fieldname === dimension['fieldname']); + let existing_filter = filters.filter(el => el.fieldname === dimension['fieldname']); - if (!found) { + if (!existing_filter.length) { filters.splice(index, 0, { "fieldname": dimension["fieldname"], "label": __(dimension["doctype"]), @@ -232,6 +232,11 @@ $.extend(erpnext.utils, { return frappe.db.get_link_options(dimension["doctype"], txt); }, }); + } else { + existing_filter[0]['fieldtype'] = "MultiSelectList"; + existing_filter[0]['get_data'] = function(txt) { + return frappe.db.get_link_options(dimension["doctype"], txt); + } } }); } diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 8b63c0f9986..da17cdeb5ae 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -306,7 +306,7 @@ def get_stock_ledger_entries(filters, items): query = query.where(sle.item_code.isin(items)) for field in ["voucher_no", "batch_no", "project", "company"]: - if filters.get(field): + if filters.get(field) and field not in inventory_dimension_fields: query = query.where(sle[field] == filters.get(field)) query = apply_warehouse_filter(query, sle, filters) From 7bd04c27c8c4b4bf8df61f4782c1c429efc4a4b4 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 20 Feb 2023 11:14:41 +0530 Subject: [PATCH 28/36] fix(ux): `ReferenceError: me is not defined` Delivery Note (cherry picked from commit 1b010add265cdc3e646fc15d78c3c39428c4ad84) --- erpnext/stock/doctype/delivery_note/delivery_note.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index ea3cf1948b3..2562c602574 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", { } if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) { - let internal = me.frm.doc.is_internal_customer; + let internal = frm.doc.is_internal_customer; if (internal) { - let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" : + let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" : "Inter Company Purchase Receipt"; - me.frm.add_custom_button(button_label, function() { + frm.add_custom_button(button_label, function() { frappe.model.open_mapped_doc({ method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', frm: frm, From bc86b6977f2c21a47d4c801f59f2613cc3ce9200 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 20 Feb 2023 11:43:32 +0530 Subject: [PATCH 29/36] chore: `Linters` (cherry picked from commit 44ee9f0f190f6761ca92f4c5036109c152bd35db) --- erpnext/stock/doctype/delivery_note/delivery_note.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 2562c602574..ae56645b730 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -102,7 +102,7 @@ frappe.ui.form.on("Delivery Note", { let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" : "Inter Company Purchase Receipt"; - frm.add_custom_button(button_label, function() { + frm.add_custom_button(__(button_label), function() { frappe.model.open_mapped_doc({ method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', frm: frm, From 7074c2b161a28de654ea9dee7bbd1bd4bc13b6db Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 21 Feb 2023 13:04:01 +0530 Subject: [PATCH 30/36] fix: fiscal year error for existing assets in fixed asset register (cherry picked from commit 76861eb332061c2e42c75e5ba42f01128024808a) --- .../assets/report/fixed_asset_register/fixed_asset_register.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 0ab3e16e424..ae99c491a93 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -151,6 +151,7 @@ def prepare_chart_data(data, filters): filters.filter_based_on, "Monthly", company=filters.company, + ignore_fiscal_year=True, ) for d in period_list: From 00eb6329a7727425d504cc36494f4affa89eb44b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:21:36 +0530 Subject: [PATCH 31/36] feat: Editable Sales Invoice (#32625) * feat: Editable Sales Invoice (cherry picked from commit 30da6ab2c1b870047d78b0f52196c906c6d3533d) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.json * chore: Update allow on submit for Sales Invoice fields (cherry picked from commit e626107d3de7f95f15627a266e652cdfedf020a1) # Conflicts: # erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json * chore: Break into smaller functions (cherry picked from commit 42e4c37f15e2a0613be9104d390bfdc3032f85b7) * chore: Update allow on submit fields (cherry picked from commit 1105e520315cbee4915e696706ecc3bfa1f0af8f) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.json * test: Add unit tests (cherry picked from commit ed98015a5639889086a7662256bf743dfe387ffc) * chore: Update tests (cherry picked from commit 1a980123a2c83c669735a87d1d59815666de6170) * chore: Reset repost_required_flag on cancel (cherry picked from commit 0966867c0835d1066edf4146156e18d7f91da2f1) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.py * chore: Enable no-copy for repost required field (cherry picked from commit 5fe55176ecc7b148380dd9940c5c34dda2aa9cd9) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.json * chore: Validate for deferred revenue invoices (cherry picked from commit e29f756146b42c203d6e2a456c5e1ebf13d2ee19) * chore: Resolve conflicts --------- Co-authored-by: Deepesh Garg --- .../doctype/sales_invoice/sales_invoice.js | 19 ++++ .../doctype/sales_invoice/sales_invoice.json | 19 +++- .../doctype/sales_invoice/sales_invoice.py | 99 ++++++++++++++++++- .../sales_invoice/test_sales_invoice.py | 26 +++++ .../sales_invoice_item.json | 8 +- .../sales_taxes_and_charges.json | 7 +- erpnext/public/js/controllers/accounts.js | 6 ++ 7 files changed, 176 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 89647da6e10..37f42c114cc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -64,6 +64,25 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); + if (this.frm.doc.repost_required && this.frm.doc.docstatus===1) { + this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update.")); + this.frm.add_custom_button(__('Repost Accounting Entries'), + () => { + this.frm.call({ + doc: this.frm.doc, + method: 'repost_accounting_entries', + freeze: true, + freeze_message: __('Reposting...'), + callback: (r) => { + if (!r.exc) { + frappe.msgprint(__('Accounting Entries are reposted')); + me.frm.refresh(); + } + } + }); + }).removeClass('btn-default').addClass('btn-warning'); + } + if (this.frm.doc.is_return) { this.frm.return_print_format = "Sales Invoice Return"; } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 0c8ddeb2bae..f18fef5a148 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -209,6 +209,7 @@ "is_internal_customer", "is_discounted", "remarks", + "repost_required", "connections_tab" ], "fields": [ @@ -1038,6 +1039,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_redemption_account", "fieldtype": "Link", @@ -1336,6 +1338,7 @@ "options": "fa fa-money" }, { + "allow_on_submit": 1, "depends_on": "is_pos", "fieldname": "cash_bank_account", "fieldtype": "Link", @@ -1435,6 +1438,7 @@ "print_hide": 1 }, { + "allow_on_submit": 1, "depends_on": "is_pos", "fieldname": "account_for_change_amount", "fieldtype": "Link", @@ -1483,6 +1487,7 @@ "hide_seconds": 1 }, { + "allow_on_submit": 1, "fieldname": "write_off_account", "fieldtype": "Link", "hide_days": 1, @@ -1706,6 +1711,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "default": "No", "fieldname": "is_opening", "fieldtype": "Select", @@ -1922,6 +1928,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "depends_on": "eval:doc.is_internal_customer", "description": "Unrealized Profit / Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", @@ -1964,6 +1971,7 @@ "label": "Disable Rounded Total" }, { + "allow_on_submit": 1, "fieldname": "additional_discount_account", "fieldtype": "Link", "label": "Discount Account", @@ -2114,6 +2122,15 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "default": "0", + "fieldname": "repost_required", + "fieldtype": "Check", + "hidden": 1, + "label": "Repost Required", + "no_copy": 1, + "read_only": 1 } ], "icon": "fa fa-file-text", @@ -2126,7 +2143,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-01-28 19:45:47.538163", + "modified": "2022-11-07 16:02:07.972258", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a56a2cf56ba..f5be4c7a3f3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -11,6 +11,9 @@ from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form import erpnext from erpnext.accounts.deferred_revenue import validate_service_stop_date +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, validate_loyalty_points, @@ -100,13 +103,11 @@ class SalesInvoice(SellingController): self.validate_debit_to_acc() self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.add_remarks() - self.validate_write_off_account() - self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() self.validate_item_cost_centers() - self.validate_income_account() self.check_conversion_rate() + self.validate_accounts() validate_inter_company_party( self.doctype, self.customer, self.company, self.inter_company_invoice_reference @@ -170,6 +171,11 @@ class SalesInvoice(SellingController): self.reset_default_field_value("set_warehouse", "items", "warehouse") + def validate_accounts(self): + self.validate_write_off_account() + self.validate_account_for_change_amount() + self.validate_income_account() + def validate_fixed_asset(self): for d in self.get("items"): if d.is_fixed_asset and d.meta.get_field("asset") and d.asset: @@ -368,6 +374,7 @@ class SalesInvoice(SellingController): self.repost_future_sle_and_gle() self.db_set("status", "Cancelled") + self.db_set("repost_required", 0) if ( frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction" @@ -516,6 +523,92 @@ class SalesInvoice(SellingController): def on_update(self): self.set_paid_amount() + def on_update_after_submit(self): + if hasattr(self, "repost_required"): + needs_repost = 0 + + # Check if any field affecting accounting entry is altered + doc_before_update = self.get_doc_before_save() + accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"] + + # Check if opening entry check updated + if doc_before_update.get("is_opening") != self.is_opening: + needs_repost = 1 + + if not needs_repost: + # Parent Level Accounts excluding party account + for field in ( + "additional_discount_account", + "cash_bank_account", + "account_for_change_amount", + "write_off_account", + "loyalty_redemption_account", + "unrealized_profit_loss_account", + ): + if doc_before_update.get(field) != self.get(field): + needs_repost = 1 + break + + # Check for parent accounting dimensions + for dimension in accounting_dimensions: + if doc_before_update.get(dimension) != self.get(dimension): + needs_repost = 1 + break + + # Check for child tables + if self.check_if_child_table_updated( + "items", + doc_before_update, + ("income_account", "expense_account", "discount_account"), + accounting_dimensions, + ): + needs_repost = 1 + + if self.check_if_child_table_updated( + "taxes", doc_before_update, ("account_head",), accounting_dimensions + ): + needs_repost = 1 + + self.validate_accounts() + + # validate if deferred revenue is enabled for any item + # Don't allow to update the invoice if deferred revenue is enabled + for item in self.get("items"): + if item.enable_deferred_revenue: + frappe.throw( + _( + "Deferred Revenue is enabled for item {0}. You cannot update the invoice after submission." + ).format(item.item_code) + ) + + self.db_set("repost_required", needs_repost) + + def check_if_child_table_updated( + self, child_table, doc_before_update, fields_to_check, accounting_dimensions + ): + # Check if any field affecting accounting entry is altered + for index, item in enumerate(self.get(child_table)): + for field in fields_to_check: + if doc_before_update.get(child_table)[index].get(field) != item.get(field): + return True + + for dimension in accounting_dimensions: + if doc_before_update.get(child_table)[index].get(dimension) != item.get(dimension): + return True + + return False + + @frappe.whitelist() + def repost_accounting_entries(self): + if self.repost_required: + self.docstatus = 2 + self.make_gl_entries_on_cancel() + self.docstatus = 1 + self.make_gl_entries() + self.db_set("repost_required", 0) + else: + frappe.throw(_("No updates pending for reposting")) + def set_paid_amount(self): paid_amount = 0.0 base_paid_amount = 0.0 diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index df3cfec24bc..f9fb6232ac9 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2769,6 +2769,31 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + # Update Invoice post submit and then check GL Entries again + + si.load_from_db() + si.items[0].income_account = "Service - _TC" + si.additional_discount_account = "_Test Account Sales - _TC" + si.taxes[0].account_head = "TDS Payable - _TC" + si.save() + + si.load_from_db() + self.assertTrue(si.repost_required) + + si.repost_accounting_entries() + + expected_gle = [ + ["_Test Account Sales - _TC", 22.0, 0.0, nowdate()], + ["Debtors - _TC", 88, 0.0, nowdate()], + ["Service - _TC", 0.0, 100.0, nowdate()], + ["TDS Payable - _TC", 0.0, 10.0, nowdate()], + ] + + check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + + si.load_from_db() + self.assertFalse(si.repost_required) + def test_asset_depreciation_on_sale_with_pro_rata(self): """ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale. @@ -3326,6 +3351,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): """select account, debit, credit, posting_date from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s + and is_cancelled = 0 order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1, diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index f4f083068ee..cfda48e5c75 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -438,6 +438,7 @@ "label": "Accounting Details" }, { + "allow_on_submit": 1, "fieldname": "income_account", "fieldtype": "Link", "label": "Income Account", @@ -450,6 +451,7 @@ "width": "120px" }, { + "allow_on_submit": 1, "fieldname": "expense_account", "fieldtype": "Link", "label": "Expense Account", @@ -469,6 +471,7 @@ "print_hide": 1 }, { + "allow_on_submit": 1, "default": ":Company", "fieldname": "cost_center", "fieldtype": "Link", @@ -800,6 +803,7 @@ "options": "Finance Book" }, { + "allow_on_submit": 1, "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -822,7 +826,6 @@ "label": "Incoming Rate (Costing)", "no_copy": 1, "options": "Company:company:default_currency", - "precision": "6", "print_hide": 1 }, { @@ -835,6 +838,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "fieldname": "discount_account", "fieldtype": "Link", "label": "Discount Account", @@ -886,7 +890,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-12-28 16:17:33.484531", + "modified": "2022-10-17 12:51:44.825398", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 3a871bfcede..e236577e118 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -51,6 +51,7 @@ "oldfieldtype": "Data" }, { + "allow_on_submit": 1, "columns": 2, "fieldname": "account_head", "fieldtype": "Link", @@ -63,6 +64,7 @@ "search_index": 1 }, { + "allow_on_submit": 1, "default": ":Company", "fieldname": "cost_center", "fieldtype": "Link", @@ -216,12 +218,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-08-05 20:04:01.726867", + "modified": "2022-10-17 13:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", "owner": "Administrator", "permissions": [], "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "states": [] } \ No newline at end of file diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index c1fe72bb488..a07f75d1c5d 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -143,6 +143,12 @@ var get_payment_mode_account = function(frm, mode_of_payment, callback) { cur_frm.cscript.account_head = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; + + if (doc.docstatus == 1) { + // Should not trigger any changes on change post submit + return; + } + if(!d.charge_type && d.account_head){ frappe.msgprint(__("Please select Charge Type first")); frappe.model.set_value(cdt, cdn, "account_head", ""); From 65aec3e4ff711ebc411fc30bc7db6288150975aa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:22:57 +0530 Subject: [PATCH 32/36] fix: Use normal rounding for Tax Withholding Category (#34114) fix: Use normal rounding for Tax Withholding Category (#34114) (cherry picked from commit 35cdd996a9e4281ed269655ff5d9e22e866e199e) Co-authored-by: Deepesh Garg --- .../tax_withholding_category.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 2c829b258b4..f0146ea70eb 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -278,7 +278,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers) if cint(tax_details.round_off_tax_amount): - tax_amount = round(tax_amount) + tax_amount = normal_round(tax_amount) return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount @@ -603,3 +603,20 @@ def is_valid_certificate( valid = True return valid + + +def normal_round(number): + """ + Rounds a number to the nearest integer. + :param number: The number to round. + """ + decimal_part = number - int(number) + + if decimal_part >= 0.5: + decimal_part = 1 + else: + decimal_part = 0 + + number = int(number) + decimal_part + + return number From 44c837f86292ce935196d757d088dd15d3f54c4a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:06:00 +0530 Subject: [PATCH 33/36] fix: Filters in item-wise sales history report (#34145) fix: Filters in item-wise sales history report (#34145) (cherry picked from commit c88444a6c488369dfbfffdd71e76d5125afa2226) Co-authored-by: Deepesh Garg --- .../report/item_wise_sales_history/item_wise_sales_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 44c4d5497ba..2624db3191d 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -216,7 +216,7 @@ def get_sales_order_details(company_list, filters): ) if filters.get("item_group"): - query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_group)) + query = query.where(db_so_item.item_group == filters.item_group) if filters.get("from_date"): query = query.where(db_so.transaction_date >= filters.from_date) @@ -225,7 +225,7 @@ def get_sales_order_details(company_list, filters): query = query.where(db_so.transaction_date <= filters.to_date) if filters.get("item_code"): - query = query.where(db_so_item.item_group == frappe.db.escape(filters.item_code)) + query = query.where(db_so_item.item_code == filters.item_code) if filters.get("customer"): query = query.where(db_so.customer == filters.customer) From 0bd765b8caa2fc7e8e2e43643f8f86934ca8fa3c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:06:45 +0530 Subject: [PATCH 34/36] refactor: clear records in batches in 'Transaction Deletion Record' (#34109) refactor: clear records in batches in 'Transaction Deletion Record' (#34109) refactor: clear records in batches (cherry picked from commit 4a7b1de2d826cdb9580388018b0571e32c8ad11d) Co-authored-by: ruthra kumar --- .../transaction_deletion_record.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index 4256a7d8312..481a3a5ebea 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -3,13 +3,17 @@ import frappe -from frappe import _ +from frappe import _, qb from frappe.desk.notifications import clear_notifications from frappe.model.document import Document -from frappe.utils import cint +from frappe.utils import cint, create_batch class TransactionDeletionRecord(Document): + def __init__(self, *args, **kwargs): + super(TransactionDeletionRecord, self).__init__(*args, **kwargs) + self.batch_size = 5000 + def validate(self): frappe.only_for("System Manager") self.validate_doctypes_to_be_ignored() @@ -155,8 +159,9 @@ class TransactionDeletionRecord(Document): "DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options" ) - for table in child_tables: - frappe.db.delete(table, {"parent": ["in", parent_docs_to_be_deleted]}) + for batch in create_batch(parent_docs_to_be_deleted, self.batch_size): + for table in child_tables: + frappe.db.delete(table, {"parent": ["in", batch]}) def delete_docs_linked_with_specified_company(self, doctype, company_fieldname): frappe.db.delete(doctype, {company_fieldname: self.company}) @@ -181,13 +186,16 @@ class TransactionDeletionRecord(Document): frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix)) def delete_version_log(self, doctype, company_fieldname): - frappe.db.sql( - """delete from `tabVersion` where ref_doctype=%s and docname in - (select name from `tab{0}` where `{1}`=%s)""".format( - doctype, company_fieldname - ), - (doctype, self.company), - ) + dt = qb.DocType(doctype) + names = qb.from_(dt).select(dt.name).where(dt[company_fieldname] == self.company).run(as_list=1) + names = [x[0] for x in names] + + if names: + versions = qb.DocType("Version") + for batch in create_batch(names, self.batch_size): + qb.from_(versions).delete().where( + (versions.ref_doctype == doctype) & (versions.docname.isin(batch)) + ).run() def delete_communications(self, doctype, company_fieldname): reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company}) @@ -199,7 +207,8 @@ class TransactionDeletionRecord(Document): ) communication_names = [c.name for c in communications] - frappe.delete_doc("Communication", communication_names, ignore_permissions=True) + for batch in create_batch(communication_names, self.batch_size): + frappe.delete_doc("Communication", batch, ignore_permissions=True) @frappe.whitelist() From 74a3588e047c2848f9cf9847304e76557737588b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:34:24 +0530 Subject: [PATCH 35/36] patch: reorder migration patches (#34071) chore: reorder migration patches. 'migrate_gl_to_payment_ledger', 'migrate_remarks_from_gl_to_payment_ledger' should always run last. (cherry picked from commit 68202639f567dd930bd9074fce1b10773fe4be12) Co-authored-by: ruthra kumar --- erpnext/patches.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 54957cd2228..adebe2b8ee9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -306,7 +306,6 @@ erpnext.patches.v13_0.set_per_billed_in_return_delivery_note execute:frappe.delete_doc("DocType", "Naming Series") erpnext.patches.v13_0.job_card_status_on_hold erpnext.patches.v14_0.copy_is_subcontracted_value_to_is_old_subcontracting_flow -erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.patches.v14_0.crm_ux_cleanup erpnext.patches.v14_0.migrate_existing_lead_notes_as_per_the_new_format erpnext.patches.v14_0.remove_india_localisation # 14-07-2022 @@ -315,10 +314,9 @@ erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022 erpnext.patches.v14_0.fix_crm_no_of_employees erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries -erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger +erpnext.patches.v13_0.update_schedule_type_in_loans erpnext.patches.v13_0.drop_unused_sle_index_parts erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization -erpnext.patches.v13_0.update_schedule_type_in_loans erpnext.patches.v14_0.update_partial_tds_fields erpnext.patches.v14_0.create_incoterms_and_migrate_shipment erpnext.patches.v14_0.setup_clear_repost_logs @@ -326,4 +324,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request erpnext.patches.v14_0.update_entry_type_for_journal_entry erpnext.patches.v14_0.change_autoname_for_tax_withheld_vouchers erpnext.patches.v14_0.update_asset_value_for_manual_depr_entries -erpnext.patches.v14_0.set_pick_list_status \ No newline at end of file +erpnext.patches.v14_0.set_pick_list_status +# below 2 migration patches should always run last +erpnext.patches.v14_0.migrate_gl_to_payment_ledger +erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger From 05d649087b1aef54c401ab6c18ada95671bed249 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 16 Feb 2023 20:42:45 +0530 Subject: [PATCH 36/36] fix: check for duplicate in pos closing and pos merge log entry (cherry picked from commit 47add0b7514b701ef43da810986c73c2852d6b97) --- .../pos_closing_entry/pos_closing_entry.py | 16 ++++++++++++++++ .../pos_invoice_merge_log.py | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index 655c4ec0035..115b415eeda 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater): if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open": frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry")) + self.validate_duplicate_pos_invoices() self.validate_pos_invoices() + def validate_duplicate_pos_invoices(self): + pos_occurences = {} + for idx, inv in enumerate(self.pos_transactions, 1): + pos_occurences.setdefault(inv.pos_invoice, []).append(idx) + + error_list = [] + for key, value in pos_occurences.items(): + if len(value) > 1: + error_list.append( + _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) + ) + + if error_list: + frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True) + def validate_pos_invoices(self): invalid_rows = [] for d in self.pos_transactions: diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 70e3baff2e4..438ff9f3c4c 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -18,6 +18,22 @@ class POSInvoiceMergeLog(Document): def validate(self): self.validate_customer() self.validate_pos_invoice_status() + self.validate_duplicate_pos_invoices() + + def validate_duplicate_pos_invoices(self): + pos_occurences = {} + for idx, inv in enumerate(self.pos_invoices, 1): + pos_occurences.setdefault(inv.pos_invoice, []).append(idx) + + error_list = [] + for key, value in pos_occurences.items(): + if len(value) > 1: + error_list.append( + _("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) + ) + + if error_list: + frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True) def validate_customer(self): if self.merge_invoices_based_on == "Customer Group": @@ -426,6 +442,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None): if closing_entry: closing_entry.set_status(update=True, status="Failed") + if type(error_message) == list: + error_message = frappe.json.dumps(error_message) closing_entry.db_set("error_message", error_message) raise