From dcf19c3ed918f18ade8e47b7a435650d0abd4280 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Feb 2024 16:50:14 +0530 Subject: [PATCH 01/22] fix: reconciliation issue due to notation difference --- erpnext/accounts/utils.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index b3e9996b515..280cf338f70 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -10,7 +10,7 @@ import frappe.defaults from frappe import _, qb, throw from frappe.model.meta import get_field_precision from frappe.query_builder import AliasedQuery, Criterion, Table -from frappe.query_builder.functions import Sum +from frappe.query_builder.functions import Round, Sum from frappe.query_builder.utils import DocType from frappe.utils import ( cint, @@ -549,16 +549,19 @@ def check_if_advance_entry_modified(args): args, ) else: - ret = frappe.db.sql( - """select name from `tabPayment Entry` - where - name = %(voucher_no)s and docstatus = 1 - and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s - and round(unallocated_amount, {1}) = round(%(unreconciled_amount)s, {1}) - """.format( - party_account_field, precision - ), - args, + pe = qb.DocType("Payment Entry") + ret = ( + qb.from_(pe) + .select(pe.name) + .where( + (pe.name == args.voucher_no) + & (pe.docstatus == 1) + & (pe.party_type == args.party_type) + & (pe.party == args.party) + & (pe[party_account_field] == args.account) + & (Round(pe.unallocated_amount, precision) == Round(args.unreconciled_amount, precision)) + ) + .run() ) if not ret: From 83bf28616e33a34174ade3504dbfae8571f8e58c Mon Sep 17 00:00:00 2001 From: kunhi Date: Mon, 12 Feb 2024 12:18:37 +0400 Subject: [PATCH 02/22] fix: fetch company terms (cherry picked from commit d97b6d38ef732205f8de17dcb01d90ea1f5120fa) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 2ac7370f4ef..9bdbff25577 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -11,6 +11,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e super.setup(doc); } company() { + super.company(); erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); let me = this; From 45a04943189c221a73f752257565cdd77c2f19ab Mon Sep 17 00:00:00 2001 From: kunhi Date: Tue, 13 Feb 2024 22:32:10 +0400 Subject: [PATCH 03/22] fix: no need call for company method in sales invoice js (cherry picked from commit e3bd8d10b0a99fca326c7252063ef11b3665d3cf) --- .../doctype/sales_invoice/sales_invoice.js | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 9bdbff25577..5ed26c7937d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -10,26 +10,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.setup_posting_date_time_check(); super.setup(doc); } - company() { - super.company(); - erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); - - let me = this; - if (this.frm.doc.company) { - frappe.call({ - method: - "erpnext.accounts.party.get_party_account", - args: { - party_type: 'Customer', - party: this.frm.doc.customer, - company: this.frm.doc.company - }, - callback: (response) => { - if (response) me.frm.set_value("debit_to", response.message); - }, - }); - } - } onload() { var me = this; super.onload(); From 5eaa11b9e871048c286c278ad7601ddf4ef171bc Mon Sep 17 00:00:00 2001 From: kunhi Date: Tue, 13 Feb 2024 22:49:39 +0400 Subject: [PATCH 04/22] fix: update_dimension is required and not need party account method (cherry picked from commit e6949d71f628a15ea87ee143d09dce47b66f0e02) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 5ed26c7937d..b2672424af9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -10,6 +10,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.setup_posting_date_time_check(); super.setup(doc); } + company() { + super.company(); + erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); + } onload() { var me = this; super.onload(); From ee9a51f93fef19b89b1d01a8f7de98237ddbefa2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 14 Feb 2024 18:45:11 +0530 Subject: [PATCH 05/22] fix: party item code in Blanket Order (cherry picked from commit 1a8f7f94036e1703ab51f8f591d88688ad2c9f52) # Conflicts: # erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py --- .../doctype/blanket_order/blanket_order.py | 39 +++++++++++++++++++ .../blanket_order/test_blanket_order.py | 25 ++++++++++++ .../blanket_order_item.json | 12 +++++- .../blanket_order_item/blanket_order_item.py | 23 +++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index 029b4b720a7..fe5f836a935 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -16,11 +16,50 @@ class BlanketOrder(Document): def validate(self): self.validate_dates() self.validate_duplicate_items() + self.set_party_item_code() def validate_dates(self): if getdate(self.from_date) > getdate(self.to_date): frappe.throw(_("From date cannot be greater than To date")) + def set_party_item_code(self): + item_ref = {} + if self.blanket_order_type == "Selling": + item_ref = self.get_customer_items_ref() + print(item_ref) + else: + item_ref = self.get_supplier_items_ref() + + if not item_ref: + return + + for row in self.items: + row.party_item_code = item_ref.get(row.item_code) + + def get_customer_items_ref(self): + items = [d.item_code for d in self.items] + + return frappe._dict( + frappe.get_all( + "Item Customer Detail", + filters={"parent": ("in", items), "customer_name": self.customer}, + fields=["parent", "ref_code"], + as_list=True, + ) + ) + + def get_supplier_items_ref(self): + items = [d.item_code for d in self.items] + + return frappe._dict( + frappe.get_all( + "Item Supplier", + filters={"parent": ("in", items), "supplier": self.supplier}, + fields=["parent", "supplier_part_no"], + as_list=True, + ) + ) + def validate_duplicate_items(self): item_list = [] for item in self.items: diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py index e9fc25b5bcb..3f3b6f092c0 100644 --- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py @@ -5,6 +5,7 @@ from frappe.tests.utils import FrappeTestCase from frappe.utils import add_months, today from erpnext import get_company_currency +from erpnext.stock.doctype.item.test_item import make_item from .blanket_order import make_order @@ -90,6 +91,30 @@ class TestBlanketOrder(FrappeTestCase): frappe.db.set_single_value("Buying Settings", "blanket_order_allowance", 10) po.submit() + def test_party_item_code(self): + item_doc = make_item("_Test Item 1 for Blanket Order") + item_code = item_doc.name + + customer = "_Test Customer" + supplier = "_Test Supplier" + + if not frappe.db.exists( + "Item Customer Detail", {"customer_name": customer, "parent": item_code} + ): + item_doc.append("customer_items", {"customer_name": customer, "ref_code": "CUST-REF-1"}) + item_doc.save() + + if not frappe.db.exists("Item Supplier", {"supplier": supplier, "parent": item_code}): + item_doc.append("supplier_items", {"supplier": supplier, "supplier_part_no": "SUPP-PART-1"}) + item_doc.save() + + # Blanket Order for Selling + bo = make_blanket_order(blanket_order_type="Selling", customer=customer, item_code=item_code) + self.assertEqual(bo.items[0].party_item_code, "CUST-REF-1") + + bo = make_blanket_order(blanket_order_type="Purchasing", supplier=supplier, item_code=item_code) + self.assertEqual(bo.items[0].party_item_code, "SUPP-PART-1") + def make_blanket_order(**args): args = frappe._dict(args) diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json index 977ad547f55..aa7831fd6b8 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2018-05-24 07:20:04.255236", "doctype": "DocType", "editable_grid": 1, @@ -6,6 +7,7 @@ "field_order": [ "item_code", "item_name", + "party_item_code", "column_break_3", "qty", "rate", @@ -62,10 +64,17 @@ "fieldname": "terms_and_conditions", "fieldtype": "Text", "label": "Terms and Conditions" + }, + { + "fieldname": "party_item_code", + "fieldtype": "Data", + "label": "Party Item Code", + "read_only": 1 } ], "istable": 1, - "modified": "2019-11-18 19:37:46.245878", + "links": [], + "modified": "2024-02-14 18:25:26.479672", "modified_by": "Administrator", "module": "Manufacturing", "name": "Blanket Order Item", @@ -74,5 +83,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py index ebce209fbc2..7260884d60c 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py @@ -6,4 +6,27 @@ from frappe.model.document import Document class BlanketOrderItem(Document): +<<<<<<< HEAD +======= + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + item_code: DF.Link + item_name: DF.Data | None + ordered_qty: DF.Float + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + party_item_code: DF.Data | None + qty: DF.Float + rate: DF.Currency + terms_and_conditions: DF.Text | None + # end: auto-generated types + +>>>>>>> 1a8f7f9403 (fix: party item code in Blanket Order) pass From 1cca51afc66d79757f7605c5d1b047811e877af4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 15 Feb 2024 14:28:06 +0530 Subject: [PATCH 06/22] chore: fix linter issue (cherry picked from commit 230a7d8d537500c0f401f21c742f2a2ad653062a) --- erpnext/manufacturing/doctype/blanket_order/blanket_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index fe5f836a935..83629092fa4 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -26,7 +26,6 @@ class BlanketOrder(Document): item_ref = {} if self.blanket_order_type == "Selling": item_ref = self.get_customer_items_ref() - print(item_ref) else: item_ref = self.get_supplier_items_ref() From ca8fb17ee849d5d6564ca15f03d32353d246c646 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 15 Feb 2024 17:32:34 +0530 Subject: [PATCH 07/22] chore: fix conflicts --- .../blanket_order_item/blanket_order_item.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py index 7260884d60c..ebce209fbc2 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py @@ -6,27 +6,4 @@ from frappe.model.document import Document class BlanketOrderItem(Document): -<<<<<<< HEAD -======= - # begin: auto-generated types - # This code is auto-generated. Do not modify anything in this block. - - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from frappe.types import DF - - item_code: DF.Link - item_name: DF.Data | None - ordered_qty: DF.Float - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - party_item_code: DF.Data | None - qty: DF.Float - rate: DF.Currency - terms_and_conditions: DF.Text | None - # end: auto-generated types - ->>>>>>> 1a8f7f9403 (fix: party item code in Blanket Order) pass From cea0e1fb91f8ae8a166af6615ed7878b885dd257 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:58:16 +0100 Subject: [PATCH 08/22] fix(Bank Transaction): precision for `(un)allocated_amount` Manual Backport of #39926 --- .../accounts/doctype/bank_transaction/bank_transaction.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 86c3a8a9336..c0a65ed0d4c 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -41,9 +41,10 @@ class BankTransaction(StatusUpdater): else: allocated_amount = 0.0 - amount = abs(flt(self.withdrawal) - flt(self.deposit)) - self.db_set("allocated_amount", flt(allocated_amount)) - self.db_set("unallocated_amount", amount - flt(allocated_amount)) + unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount + + self.db_set("allocated_amount", flt(allocated_amount, self.precision("allocated_amount"))) + self.db_set("unallocated_amount", flt(unallocated_amount, self.precision("unallocated_amount"))) self.reload() self.set_status(update=True) From 8deaba8defd483bf9027bd03c58154f01f20fb24 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:58:58 +0100 Subject: [PATCH 09/22] fix(Issue): create communication Ignore permisions and mandatory. Required, for example, when Issue is created by Customer via portal. (cherry picked from commit 3f1d008741e1255b2b9c0d31aa489d9b427ab5c7) --- erpnext/support/doctype/issue/issue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 7f3e0cf4c21..25041b1973b 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,8 +72,8 @@ class Issue(Document): "reference_name": self.name, } ) - communication.ignore_permissions = True - communication.ignore_mandatory = True + communication.flags.ignore_permissions = True + communication.flags.ignore_mandatory = True communication.save() @frappe.whitelist() From 5894bb3bbe2a7c6b52b52aaaa19412904ac49cca Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 17 Feb 2024 07:19:32 +0530 Subject: [PATCH 10/22] refactor: use popup to inform on additional reconciliation step (cherry picked from commit 0d260faa0070f767220ba724dd42b0f2d2cc922d) --- erpnext/controllers/accounts_controller.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2420d987c23..f642cde73c3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -201,6 +201,18 @@ class AccountsController(TransactionBase): ) ) + if self.get("is_return") and self.get("return_against"): + document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note" + frappe.msgprint( + _( + "{0} will be treated as a standalone {0}. Post creation use {1} tool to reconcile against {2}." + ).format( + document_type, + get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), + get_link_to_form(self.doctype, self.get("return_against")), + ) + ) + pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid" if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)): self.set_advances() From b1a4249041b1834965cc719ac733423a198fe615 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 19 Feb 2024 07:44:02 +0530 Subject: [PATCH 11/22] fix: typeerror 'Item Group' filter on Purchase Register --- .../item_wise_purchase_register/item_wise_purchase_register.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 5d3d4d74978..b5dd66f75fe 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -319,7 +319,8 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice`.unrealized_profit_loss_account, `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, - `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group, + `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` + ,`tabPurchase Invoice Item`.`item_group` as pi_item_group, `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, From 4921b038bdcfef61191bb31c97e6583a564f3589 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 17 Feb 2024 06:41:00 +0530 Subject: [PATCH 12/22] fix: group node in warehouse filter in Item-wise Sales Register (cherry picked from commit 44538bd02aa150844135d21934bf403ff5244e68) --- .../item_wise_sales_register/item_wise_sales_register.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index ce22d7566c1..94457d59981 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -350,7 +350,13 @@ def get_conditions(filters, additional_conditions=None): and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" if filters.get("warehouse"): - conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" + if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"): + lft, rgt = frappe.db.get_all( + "Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True + )[0] + conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) " + else: + conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" if filters.get("brand"): conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s""" From 241507a87ff108eabcc8fb7422a1dd80a9836e26 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 15 Feb 2024 12:35:14 +0530 Subject: [PATCH 13/22] refactor: add total row if only one party is being filtered (cherry picked from commit b1dfa2537bee220e8bb915b8152e4aa3014befa9) --- .../report/accounts_receivable/accounts_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 9219cc5e383..88e26863cf1 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -85,7 +85,10 @@ class ReceivablePayableReport(object): self.skip_total_row = 1 if self.filters.get("in_party_currency"): - self.skip_total_row = 1 + if self.filters.get("party") and len(self.filters.get("party")) == 1: + self.skip_total_row = 0 + else: + self.skip_total_row = 1 def get_data(self): self.get_ple_entries() From d913ec52db9d90a8eae519e2dd2eaa5b657bb0c7 Mon Sep 17 00:00:00 2001 From: Nitali Mittal <51705792+nitmit@users.noreply.github.com> Date: Mon, 19 Feb 2024 16:36:23 +0530 Subject: [PATCH 14/22] feat: New financial views - Growth and margin views for P&L and balance sheet feat: New financial views - Growth and margin views for P&L and balance sheet --- .../report/balance_sheet/balance_sheet.js | 13 +++++ .../profit_and_loss_statement.js | 15 ++++++ erpnext/public/js/financial_statements.js | 50 +++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index f1f8e5f6e7c..57de73e72d0 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -6,6 +6,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") } + ], + "default": "Report", + "reqd": 1 + }, + ); frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index e794f270c2b..4e5cd312734 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -8,6 +8,21 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") }, + { "value": "Margin", "label": __("Margin View") }, + ], + "default": "Report", + "reqd": 1 + }, + ); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "include_default_book_entries", diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 5e1974299ee..666505e2e38 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -2,7 +2,57 @@ frappe.provide("erpnext.financial_statements"); erpnext.financial_statements = { "filters": get_filters(), + "baseData": null, "formatter": function(value, row, column, data, default_formatter, filter) { + if(frappe.query_report.get_filter_value("selected_view") == "Growth" && data && column.colIndex >= 3){ + //Assuming that the first three columns are s.no, account name and the very first year of the accounting values, to calculate the relative percentage values of the successive columns. + const lastAnnualValue = row[column.colIndex - 1].content; + const currentAnnualvalue = data[column.fieldname]; + if(currentAnnualvalue == undefined) return 'NA'; //making this not applicable for undefined/null values + let annualGrowth = 0; + if(lastAnnualValue == 0 && currentAnnualvalue > 0){ + //If the previous year value is 0 and the current value is greater than 0 + annualGrowth = 1; + } + else if(lastAnnualValue > 0){ + annualGrowth = (currentAnnualvalue - lastAnnualValue) / lastAnnualValue; + } + + const growthPercent = (Math.round(annualGrowth*10000)/100); //calculating the rounded off percentage + + value = $(`${((growthPercent >=0)? '+':'' )+growthPercent+'%'}`); + if(growthPercent < 0){ + value = $(value).addClass("text-danger"); + } + else{ + value = $(value).addClass("text-success"); + } + value = $(value).wrap("

").parent().html(); + + return value; + } + else if(frappe.query_report.get_filter_value("selected_view") == "Margin" && data){ + if(column.fieldname =="account" && data.account_name == __("Income")){ + //Taking the total income from each column (for all the financial years) as the base (100%) + this.baseData = row; + } + if(column.colIndex >= 2){ + //Assuming that the first two columns are s.no and account name, to calculate the relative percentage values of the successive columns. + const currentAnnualvalue = data[column.fieldname]; + const baseValue = this.baseData[column.colIndex].content; + if(currentAnnualvalue == undefined || baseValue <= 0) return 'NA'; + const marginPercent = Math.round((currentAnnualvalue/baseValue)*10000)/100; + + value = $(`${marginPercent+'%'}`); + if(marginPercent < 0) + value = $(value).addClass("text-danger"); + else + value = $(value).addClass("text-success"); + value = $(value).wrap("

").parent().html(); + return value; + } + + } if (data && column.fieldname=="account") { value = data.account_name || value; From abceb1b6110953bca1609e739a0a835542b6b946 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 12:21:12 +0530 Subject: [PATCH 15/22] fix: linter issue --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index a347ece5f5b..8db401bcc59 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -77,7 +77,7 @@ class AssetCapitalization(StockController): "Stock Ledger Entry", "Repost Item Valuation", "Asset", - "Asset Movement" + "Asset Movement", ) self.cancel_target_asset() self.update_stock_ledger() From 963ddac528daa6ac9694757af704282f9bd22262 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 Feb 2024 09:40:57 +0530 Subject: [PATCH 16/22] fix: Adjust amount in last row due to rounding --- erpnext/assets/doctype/asset/asset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3fcb9720dd1..4560d098cd7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -513,14 +513,14 @@ class Asset(AccountsController): ) # Adjust depreciation amount in the last period based on the expected value after useful life - if finance_book.expected_value_after_useful_life and ( + if ( ( n == cint(final_number_of_depreciations) - 1 - and value_after_depreciation != finance_book.expected_value_after_useful_life + and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) ) - or value_after_depreciation < finance_book.expected_value_after_useful_life + or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life) ): - depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life + depreciation_amount += flt(value_after_depreciation) - flt(finance_book.expected_value_after_useful_life) skip_row = True if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0: From e99485bfa7d78f99094b08d5147f02cc3921cb12 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 11:57:06 +0530 Subject: [PATCH 17/22] fix: linter issues --- erpnext/assets/doctype/asset/asset.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 4560d098cd7..7f5e4debf29 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -514,13 +514,14 @@ class Asset(AccountsController): # Adjust depreciation amount in the last period based on the expected value after useful life if ( - ( - n == cint(final_number_of_depreciations) - 1 - and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) - ) - or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life) + n == cint(final_number_of_depreciations) - 1 + and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) + ) or flt(value_after_depreciation) < flt( + finance_book.expected_value_after_useful_life ): - depreciation_amount += flt(value_after_depreciation) - flt(finance_book.expected_value_after_useful_life) + depreciation_amount += flt(value_after_depreciation) - flt( + finance_book.expected_value_after_useful_life + ) skip_row = True if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0: From a4fbea3722ef61e7d79e8158629e564f6f32487a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 12:31:34 +0530 Subject: [PATCH 18/22] fix: linter issue --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 8db401bcc59..2b5d8d0ffdd 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -83,7 +83,7 @@ class AssetCapitalization(StockController): self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() - + def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) From 385b08dc50ba27f00fd1e71f8e3156a813ca7219 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:13:35 +0530 Subject: [PATCH 19/22] fix: show active bom in the dropdown while making stock entry and MR (backport #39974) (#39975) fix: show active bom in the dropdown while making stock entry and MR (#39974) (cherry picked from commit 133f8bd92a33fb15d1ababf8b5f1d4f061426538) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.js | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 4dc8e6b9cbd..c5bc601e391 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -250,7 +250,7 @@ frappe.ui.form.on('Material Request', { fields: [ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"), options:"BOM", reqd: 1, get_query: function() { - return {filters: { docstatus:1 }}; + return {filters: { docstatus:1, "is_active": 1 }}; }}, {"fieldname":"warehouse", "fieldtype":"Link", "label":__("For Warehouse"), options:"Warehouse", reqd: 1}, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 0786ce6be2a..b6459ba1e28 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -548,7 +548,9 @@ frappe.ui.form.on('Stock Entry', { let fields = [ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"), - options:"BOM", reqd: 1, get_query: filters()}, + options:"BOM", reqd: 1, get_query: () => { + return {filters: { docstatus:1, "is_active": 1 }}; + }}, {"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"), options:"Warehouse"}, {"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"), From 8e71665e4f19cddedc534c7e7996d76a6d83d0cc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:53:20 +0530 Subject: [PATCH 20/22] fix: 'NoneType' object is not iterable (backport #39977) (#39980) fix: 'NoneType' object is not iterable (#39977) (cherry picked from commit 8e7d47b3a7453ce7ded83c4205c556716f2afa19) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/pick_list/pick_list.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 36c877ebc88..86462476a5d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -32,6 +32,10 @@ class PickList(Document): self.update_status() self.set_item_locations() + if self.get("locations"): + self.validate_sales_order_percentage() + + def validate_sales_order_percentage(self): # set percentage picked in SO for location in self.get("locations"): if ( From 5b9905f27ab80dcc1b74b22d7dfa5eb52b710c2a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 17:54:58 +0530 Subject: [PATCH 21/22] fix: On cancelation of capitalization, reverse depreciation entry only if journal entry exists --- erpnext/assets/doctype/asset/depreciation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 6523713ece0..22bd6b1d6ff 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -520,7 +520,7 @@ def reverse_depreciation_entry_made_after_disposal(asset, date): else: row += 1 - if schedule.schedule_date == date: + if schedule.schedule_date == date and schedule.journal_entry: if not disposal_was_made_on_original_schedule_date( asset, schedule, row, date ) or disposal_happens_in_the_future(date): From 64099b0bf75ff6574a65e49be0f39feddeeedecb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 21 Feb 2024 09:26:58 +0530 Subject: [PATCH 22/22] fix: webpages are not showing (#39988) --- erpnext/utilities/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 963cd2bf8f0..69559c1ed9b 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -51,8 +51,8 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): .where((BIN.item_code == item_code) & (BIN.warehouse == warehouse)) ).run() - stock_qty = stock_qty[0][0] if stock_qty: + stock_qty = flt(stock_qty[0][0]) total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse) in_stock = int(total_stock > 0)