From 6e833cce6af9d9542c23e74bda7c0b0e029a683e Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:22:40 +0530 Subject: [PATCH 01/44] fix: Update Rate as per Valuation Rate for Internal Transfers only if Setting is Enabled (#42050) * fix: update rate for internal transfers only if settings enabled * fix: better naming * fix: create field for storing incoming rate in purchase doctypes * fix: use qty instead of qty_in_stock_uom * fix: add description, refactor for readablility * test: test case to validate internal transfers at arm's length price * fix: minor fix * fix: deletion of code not required --------- Co-authored-by: Smit Vora (cherry picked from commit 723ac0ffc4d25dc901c8c1b33a7b1b220682e642) --- .../purchase_invoice_item.json | 13 +- .../purchase_invoice_item.py | 1 + erpnext/controllers/buying_controller.py | 134 ++++++++++-------- erpnext/controllers/selling_controller.py | 6 + .../tests/test_accounts_controller.py | 38 ++++- .../purchase_receipt_item.json | 13 +- .../purchase_receipt_item.py | 1 + .../stock_settings/stock_settings.json | 8 ++ .../doctype/stock_settings/stock_settings.py | 1 + 9 files changed, 154 insertions(+), 61 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 1d7b0c2f461..8a2ba36cf62 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -57,6 +57,7 @@ "base_net_rate", "base_net_amount", "valuation_rate", + "sales_incoming_rate", "item_tax_amount", "landed_cost_voucher_amount", "rm_supp_cost", @@ -958,12 +959,22 @@ "print_hide": 1, "read_only": 1, "search_index": 1 + }, + { + "description": "Valuation rate for the item as per Sales Invoice (Only for Internal Transfers)", + "fieldname": "sales_incoming_rate", + "fieldtype": "Currency", + "hidden": 1, + "label": "Sales Incoming Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-06-14 11:57:07.171700", + "modified": "2024-07-19 12:12:42.449298", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py index baeece4815c..a8f844c6c1c 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py @@ -79,6 +79,7 @@ class PurchaseInvoiceItem(Document): rejected_serial_no: DF.Text | None rejected_warehouse: DF.Link | None rm_supp_cost: DF.Currency + sales_incoming_rate: DF.Currency sales_invoice_item: DF.Data | None serial_and_batch_bundle: DF.Link | None serial_no: DF.Text | None diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 958ac266e61..a55eded2a4c 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -314,18 +314,22 @@ class BuyingController(SubcontractingController): get_conversion_factor(item.item_code, item.uom).get("conversion_factor") or 1.0 ) + net_rate = item.base_net_amount + if item.sales_incoming_rate: # for internal transfer + net_rate = item.qty * item.sales_incoming_rate + qty_in_stock_uom = flt(item.qty * item.conversion_factor) if self.get("is_old_subcontracting_flow"): item.rm_supp_cost = self.get_supplied_items_cost(item.name, reset_outgoing_rate) item.valuation_rate = ( - item.base_net_amount + net_rate + item.item_tax_amount + item.rm_supp_cost + flt(item.landed_cost_voucher_amount) ) / qty_in_stock_uom else: item.valuation_rate = ( - item.base_net_amount + net_rate + item.item_tax_amount + flt(item.landed_cost_voucher_amount) + flt(item.get("rate_difference_with_purchase_invoice")) @@ -336,72 +340,88 @@ class BuyingController(SubcontractingController): update_regional_item_valuation_rate(self) def set_incoming_rate(self): - if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"): + """ + Override item rate with incoming rate for internal stock transfer + """ + if self.doctype not in ("Purchase Receipt", "Purchase Invoice"): + return + + if not (self.doctype == "Purchase Receipt" or self.get("update_stock")): + return + + if cint(self.get("is_return")): + # Get outgoing rate based on original item cost based on valuation method return if not self.is_internal_transfer(): return + allow_at_arms_length_price = frappe.get_cached_value( + "Stock Settings", None, "allow_internal_transfer_at_arms_length_price" + ) + if allow_at_arms_length_price: + return + + self.set_sales_incoming_rate_for_internal_transfer() + + for d in self.get("items"): + d.discount_percentage = 0.0 + d.discount_amount = 0.0 + d.margin_rate_or_amount = 0.0 + + if d.rate == d.sales_incoming_rate: + continue + + d.rate = d.sales_incoming_rate + frappe.msgprint( + _( + "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" + ).format(d.idx), + alert=1, + ) + + def set_sales_incoming_rate_for_internal_transfer(self): + """ + Set incoming rate from the sales transaction against which the + purchase is made (internal transfer) + """ ref_doctype_map = { - "Purchase Order": "Sales Order Item", "Purchase Receipt": "Delivery Note Item", "Purchase Invoice": "Sales Invoice Item", } ref_doctype = ref_doctype_map.get(self.doctype) - items = self.get("items") - for d in items: - if not cint(self.get("is_return")): - # Get outgoing rate based on original item cost based on valuation method + for d in self.get("items"): + if not d.get(frappe.scrub(ref_doctype)): + posting_time = self.get("posting_time") + if not posting_time: + posting_time = nowtime() - if not d.get(frappe.scrub(ref_doctype)): - posting_time = self.get("posting_time") - if not posting_time and self.doctype == "Purchase Order": - posting_time = nowtime() + outgoing_rate = get_incoming_rate( + { + "item_code": d.item_code, + "warehouse": d.get("from_warehouse"), + "posting_date": self.get("posting_date") or self.get("transaction_date"), + "posting_time": posting_time, + "qty": -1 * flt(d.get("stock_qty")), + "serial_and_batch_bundle": d.get("serial_and_batch_bundle"), + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + "allow_zero_valuation": d.get("allow_zero_valuation"), + "voucher_detail_no": d.name, + }, + raise_error_if_no_rate=False, + ) - outgoing_rate = get_incoming_rate( - { - "item_code": d.item_code, - "warehouse": d.get("from_warehouse"), - "posting_date": self.get("posting_date") or self.get("transaction_date"), - "posting_time": posting_time, - "qty": -1 * flt(d.get("stock_qty")), - "serial_and_batch_bundle": d.get("serial_and_batch_bundle"), - "company": self.company, - "voucher_type": self.doctype, - "voucher_no": self.name, - "allow_zero_valuation": d.get("allow_zero_valuation"), - "voucher_detail_no": d.name, - }, - raise_error_if_no_rate=False, - ) - - rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate")) - else: - field = ( - "incoming_rate" - if self.get("is_internal_supplier") and not self.doctype == "Purchase Order" - else "rate" - ) - rate = flt( - frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field) - * (d.conversion_factor or 1), - d.precision("rate"), - ) - - if self.is_internal_transfer(): - if self.doctype == "Purchase Receipt" or self.get("update_stock"): - if rate != d.rate: - d.rate = rate - frappe.msgprint( - _( - "Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer" - ).format(d.idx), - alert=1, - ) - d.discount_percentage = 0.0 - d.discount_amount = 0.0 - d.margin_rate_or_amount = 0.0 + d.sales_incoming_rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate")) + else: + field = "incoming_rate" if self.get("is_internal_supplier") else "rate" + d.sales_incoming_rate = flt( + frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field) + * (d.conversion_factor or 1), + d.precision("rate"), + ) def validate_for_subcontracting(self): if self.is_subcontracted and self.get("is_old_subcontracting_flow"): @@ -566,11 +586,9 @@ class BuyingController(SubcontractingController): if d.from_warehouse: sle.dependant_sle_voucher_detail_no = d.name else: - val_rate_db_precision = 6 if cint(self.precision("valuation_rate", d)) <= 6 else 9 - incoming_rate = flt(d.valuation_rate, val_rate_db_precision) sle.update( { - "incoming_rate": incoming_rate, + "incoming_rate": d.valuation_rate, "recalculate_rate": 1 if (self.is_subcontracted and (d.bom or d.get("fg_item"))) or d.from_warehouse else 0, diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 7a85d2230f1..9f1ebdf7fb1 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -435,6 +435,9 @@ class SellingController(StockController): if self.doctype not in ("Delivery Note", "Sales Invoice"): return + allow_at_arms_length_price = frappe.get_cached_value( + "Stock Settings", None, "allow_internal_transfer_at_arms_length_price" + ) items = self.get("items") + (self.get("packed_items") or []) for d in items: if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"): @@ -481,6 +484,9 @@ class SellingController(StockController): if d.incoming_rate != incoming_rate: d.incoming_rate = incoming_rate else: + if allow_at_arms_length_price: + continue + rate = flt( flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, d.precision("rate"), diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 3f6830c2021..b2f8fce3d31 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -5,7 +5,7 @@ import frappe from frappe import qb from frappe.query_builder.functions import Sum -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, getdate, nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -13,6 +13,7 @@ from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_pay from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.party import get_party_account +from erpnext.buying.doctype.purchase_order.test_purchase_order import prepare_data_for_internal_transfer from erpnext.stock.doctype.item.test_item import create_item @@ -804,6 +805,41 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) + @change_settings("Stock Settings", {"allow_internal_transfer_at_arms_length_price": 1}) + def test_16_internal_transfer_at_arms_length_price(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + prepare_data_for_internal_transfer() + company = "_Test Company with perpetual inventory" + target_warehouse = create_warehouse("_Test Internal Warehouse New 1", company=company) + warehouse = create_warehouse("_Test Internal Warehouse New 2", company=company) + arms_length_price = 40 + + si = create_sales_invoice( + company=company, + customer="_Test Internal Customer 2", + debit_to="Debtors - TCP1", + target_warehouse=target_warehouse, + warehouse=warehouse, + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + update_stock=True, + do_not_save=True, + do_not_submit=True, + ) + + si.items[0].rate = arms_length_price + si.save() + # rate should not reset to incoming rate + self.assertEqual(si.items[0].rate, arms_length_price) + + frappe.db.set_single_value("Stock Settings", "allow_internal_transfer_at_arms_length_price", 0) + si.items[0].rate = arms_length_price + si.save() + # rate should reset to incoming rate + self.assertEqual(si.items[0].rate, 100) + def test_20_journal_against_sales_invoice(self): # Invoice in Foreign Currency si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1) 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 fd26b611f45..610bceddf0f 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -67,6 +67,7 @@ "base_net_rate", "base_net_amount", "valuation_rate", + "sales_incoming_rate", "item_tax_amount", "rm_supp_cost", "landed_cost_voucher_amount", @@ -1124,12 +1125,22 @@ "fieldtype": "Check", "label": "Return Qty from Rejected Warehouse", "read_only": 1 + }, + { + "description": "Valuation rate for the item as per Sales Invoice (Only for Internal Transfers)", + "fieldname": "sales_incoming_rate", + "fieldtype": "Currency", + "hidden": 1, + "label": "Sales Incoming Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-05-28 09:48:24.448815", + "modified": "2024-07-19 12:14:21.521466", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py index 393b6a25691..2154007771d 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.py @@ -88,6 +88,7 @@ class PurchaseReceiptItem(Document): return_qty_from_rejected_warehouse: DF.Check returned_qty: DF.Float rm_supp_cost: DF.Currency + sales_incoming_rate: DF.Currency sales_order: DF.Link | None sales_order_item: DF.Data | None sample_quantity: DF.Int diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 431fdf681ff..ca08f6f0385 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -32,6 +32,7 @@ "allow_negative_stock", "show_barcode_field", "clean_description_html", + "allow_internal_transfer_at_arms_length_price", "quality_inspection_settings_section", "action_if_quality_inspection_is_not_submitted", "column_break_23", @@ -440,6 +441,13 @@ "fieldtype": "Check", "label": "Do Not Update Serial / Batch on Creation of Auto Bundle" }, + { + "default": "0", + "description": "If enabled, the item rate won't adjust to the valuation rate during internal transfers, but accounting will still use the valuation rate.", + "fieldname": "allow_internal_transfer_at_arms_length_price", + "fieldtype": "Check", + "label": "Allow Internal Transfers at Arm's Length Price" + }, { "default": "0", "depends_on": "eval:doc.valuation_method === \"Moving Average\"", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index fae75f49777..c029b7bd1fb 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -27,6 +27,7 @@ class StockSettings(Document): action_if_quality_inspection_is_rejected: DF.Literal["Stop", "Warn"] allow_from_dn: DF.Check allow_from_pr: DF.Check + allow_internal_transfer_at_arms_length_price: DF.Check allow_negative_stock: DF.Check allow_partial_reservation: DF.Check allow_to_edit_stock_uom_qty_for_purchase: DF.Check From 87d8603d1d70c4a1d169322d99de3218a16ad5da Mon Sep 17 00:00:00 2001 From: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:19:54 +0530 Subject: [PATCH 02/44] fix: promotional scheme doctype fields in consitency with pricing rule (#42432) * fix: add "round_free_qty" check box in promotional scheme * fix: add `add_for_price_list` field * fix: set_query in setup for promotional scheme --------- (cherry picked from commit 8624aeca54eacf4b32e86b8d1d82245fcef65355) # Conflicts: # erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json # erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json --- .../promotional_scheme/promotional_scheme.js | 12 ++++++++++++ .../promotional_scheme/promotional_scheme.py | 2 ++ .../promotional_scheme_price_discount.json | 12 ++++++++++++ .../promotional_scheme_price_discount.py | 1 + .../promotional_scheme_product_discount.json | 13 ++++++++++++- .../promotional_scheme_product_discount.py | 1 + 6 files changed, 40 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js index 7a26c07d01f..43261e4080a 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js @@ -2,6 +2,18 @@ // For license information, please see license.txt frappe.ui.form.on("Promotional Scheme", { + setup: function (frm) { + frm.set_query("for_price_list", "price_discount_slabs", (doc) => { + return { + filters: { + selling: doc.selling, + buying: doc.buying, + currency: doc.currency, + }, + }; + }); + }, + refresh: function (frm) { frm.trigger("set_options_for_applicable_for"); frm.trigger("toggle_reqd_apply_on"); diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 71fe156dd7a..86bd2135515 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -51,6 +51,7 @@ price_discount_fields = [ "discount_percentage", "validate_applied_rule", "apply_multiple_pricing_rules", + "for_price_list", ] product_discount_fields = [ @@ -63,6 +64,7 @@ product_discount_fields = [ "recurse_for", "apply_recursion_over", "apply_multiple_pricing_rules", + "round_free_qty", ] diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index aa3696d216d..fc7cd6e0921 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -21,6 +21,7 @@ "rate", "discount_amount", "discount_percentage", + "for_price_list", "section_break_11", "warehouse", "threshold_percentage", @@ -120,6 +121,13 @@ "fieldtype": "Float", "label": "Discount Percentage" }, + { + "depends_on": "eval:doc.rate_or_discount!=\"Rate\"", + "fieldname": "for_price_list", + "fieldtype": "Link", + "label": "For Price List", + "options": "Price List" + }, { "fieldname": "section_break_11", "fieldtype": "Section Break" @@ -169,7 +177,11 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2021-11-16 00:25:33.843996", +======= + "modified": "2024-07-23 12:33:46.574950", +>>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py index 545c17dda89..3e07309dca5 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.py @@ -19,6 +19,7 @@ class PromotionalSchemePriceDiscount(Document): disable: DF.Check discount_amount: DF.Currency discount_percentage: DF.Float + for_price_list: DF.Link | None max_amount: DF.Currency max_qty: DF.Float min_amount: DF.Currency diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json index 4e61d04ad21..75bcad8c2c4 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json @@ -22,6 +22,7 @@ "column_break_9", "free_item_uom", "free_item_rate", + "round_free_qty", "section_break_12", "warehouse", "threshold_percentage", @@ -181,12 +182,22 @@ "fieldtype": "Float", "label": "Apply Recursion Over (As Per Transaction UOM)", "mandatory_depends_on": "is_recursive" + }, + { + "default": "0", + "fieldname": "round_free_qty", + "fieldtype": "Check", + "label": "Round Free Qty" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-03-12 12:53:58.199108", +======= + "modified": "2024-07-22 17:25:07.880984", +>>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Product Discount", @@ -195,4 +206,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py index 1463a7b93a4..b071d658cb4 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.py @@ -53,6 +53,7 @@ class PromotionalSchemeProductDiscount(Document): "20", ] recurse_for: DF.Float + round_free_qty: DF.Check rule_description: DF.SmallText same_item: DF.Check threshold_percentage: DF.Percent From 97cadfe5d360128934aaaac9346537a47515cf75 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 23 Jul 2024 17:44:37 +0530 Subject: [PATCH 03/44] fix: allow sale of asset for internal transfer (cherry picked from commit 972329cc166c18138cf5c745755144235b158679) --- .../doctype/sales_invoice/sales_invoice.py | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 24e61b0d221..a0eef4a4f3e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1316,6 +1316,10 @@ class SalesInvoice(SellingController): for item in self.get("items"): if flt(item.base_net_amount, item.precision("base_net_amount")): + # Do not book income for transfer within same company + if self.is_internal_transfer(): + continue + if item.is_fixed_asset: asset = self.get_asset(item) @@ -1374,37 +1378,33 @@ class SalesInvoice(SellingController): self.set_asset_status(asset) else: - # Do not book income for transfer within same company - if not self.is_internal_transfer(): - income_account = ( - item.income_account - if (not item.enable_deferred_revenue or self.is_return) - else item.deferred_revenue_account - ) + income_account = ( + item.income_account + if (not item.enable_deferred_revenue or self.is_return) + else item.deferred_revenue_account + ) - amount, base_amount = self.get_amount_and_base_amount( - item, enable_discount_accounting - ) + amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting) - account_currency = get_account_currency(income_account) - gl_entries.append( - self.get_gl_dict( - { - "account": income_account, - "against": self.customer, - "credit": flt(base_amount, item.precision("base_net_amount")), - "credit_in_account_currency": ( - flt(base_amount, item.precision("base_net_amount")) - if account_currency == self.company_currency - else flt(amount, item.precision("net_amount")) - ), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - account_currency, - item=item, - ) + account_currency = get_account_currency(income_account) + gl_entries.append( + self.get_gl_dict( + { + "account": income_account, + "against": self.customer, + "credit": flt(base_amount, item.precision("base_net_amount")), + "credit_in_account_currency": ( + flt(base_amount, item.precision("base_net_amount")) + if account_currency == self.company_currency + else flt(amount, item.precision("net_amount")) + ), + "cost_center": item.cost_center, + "project": item.project or self.project, + }, + account_currency, + item=item, ) + ) # expense account gl entries if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company): From 78eb4436149c5eeff71ee78f0d56cfa433f4f937 Mon Sep 17 00:00:00 2001 From: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Date: Wed, 31 Jul 2024 13:38:38 +0530 Subject: [PATCH 04/44] chore: resolve conflicts (#42553) --- .../promotional_scheme_price_discount.json | 6 +----- .../promotional_scheme_product_discount.json | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index fc7cd6e0921..32de0cec6fe 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -177,11 +177,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2021-11-16 00:25:33.843996", -======= "modified": "2024-07-23 12:33:46.574950", ->>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", @@ -189,4 +185,4 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json index 75bcad8c2c4..50d3ccb0dec 100644 --- a/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json @@ -193,11 +193,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-03-12 12:53:58.199108", -======= "modified": "2024-07-22 17:25:07.880984", ->>>>>>> 8624aeca54 (fix: promotional scheme doctype fields in consitency with pricing rule (#42432)) "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Product Discount", From fd4143e6862b3caa38d3f579bb2402be83f313fc Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 31 Jul 2024 18:49:19 +0530 Subject: [PATCH 05/44] fix: dimensions in common party journal entry (cherry picked from commit ac629ede796a27bf0d8eaaa862baae1f1e39dd47) --- erpnext/controllers/accounts_controller.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index dc65d76a357..63d0404642a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2433,6 +2433,15 @@ class AccountsController(TransactionBase): advance_entry.cost_center = self.cost_center or erpnext.get_default_cost_center(self.company) advance_entry.is_advance = "Yes" + # update dimesions + dimensions_dict = frappe._dict() + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = self.get(dim.fieldname) + + reconcilation_entry.update(dimensions_dict) + advance_entry.update(dimensions_dict) + if self.doctype == "Sales Invoice": reconcilation_entry.credit_in_account_currency = self.outstanding_amount advance_entry.debit_in_account_currency = self.outstanding_amount From 838d341d919dcf25b85660401e6ed4a036c88a28 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 6 Aug 2024 17:46:19 +0530 Subject: [PATCH 06/44] test: dimension inheritance on Exc Gain/Loss JV on Common party (cherry picked from commit 8040544216eb93bbca209a7120dd612b4e6f4e31) --- .../sales_invoice/test_sales_invoice.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 9197f2e091a..4e51942ed93 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3083,6 +3083,84 @@ class TestSalesInvoice(FrappeTestCase): party_link.delete() frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + def test_sales_invoice_against_supplier_usd_with_dimensions(self): + from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import ( + make_customer, + ) + from erpnext.accounts.doctype.party_link.party_link import create_party_link + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + # create a customer + customer = make_customer(customer="_Test Common Supplier USD") + cust_doc = frappe.get_doc("Customer", customer) + cust_doc.default_currency = "USD" + cust_doc.save() + # create a supplier + supplier = create_supplier(supplier_name="_Test Common Supplier USD").name + supp_doc = frappe.get_doc("Supplier", supplier) + supp_doc.default_currency = "USD" + supp_doc.save() + + # create a party link between customer & supplier + party_link = create_party_link("Supplier", supplier, customer) + + # enable common party accounting + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1) + + # create a dimension and make it mandatory + if not frappe.get_all("Accounting Dimension", filters={"document_type": "Department"}): + dim = frappe.get_doc( + { + "doctype": "Accounting Dimension", + "document_type": "Department", + "dimension_defaults": [{"company": "_Test Company", "mandatory_for_bs": True}], + } + ) + dim.save() + else: + dim = frappe.get_doc( + "Accounting Dimension", + frappe.get_all("Accounting Dimension", filters={"document_type": "Department"})[0], + ) + dim.disabled = False + dim.dimension_defaults = [] + dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True}) + dim.save() + + # create a sales invoice + si = create_sales_invoice( + customer=customer, parent_cost_center="_Test Cost Center - _TC", do_not_submit=True + ) + si.department = "All Departments" + si.save().submit() + + # check outstanding of sales invoice + si.reload() + self.assertEqual(si.status, "Paid") + self.assertEqual(flt(si.outstanding_amount), 0.0) + + # check creation of journal entry + jv = frappe.get_all( + "Journal Entry Account", + { + "account": si.debit_to, + "party_type": "Customer", + "party": si.customer, + "reference_type": si.doctype, + "reference_name": si.name, + "department": "All Departments", + }, + pluck="credit_in_account_currency", + ) + + self.assertTrue(jv) + self.assertEqual(jv[0], si.grand_total) + + dim.disabled = True + dim.save() + party_link.delete() + frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0) + def test_payment_statuses(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry From 3cac4a598fdead4f35f4130a899950625f51f167 Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Thu, 1 Aug 2024 17:01:43 +0200 Subject: [PATCH 07/44] feat: changes in opportunity.py to show contacts and addresses from referenced and opportunities (cherry picked from commit 61576ca03092a2a60eb5d468ee62495a868673ad) --- erpnext/crm/doctype/opportunity/opportunity.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index db9d31b53bb..16de1208de5 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -93,7 +93,20 @@ class Opportunity(TransactionBase, CRMNote): def onload(self): ref_doc = frappe.get_doc(self.opportunity_from, self.party_name) + load_address_and_contact(ref_doc) + load_address_and_contact(self) + + ref_doc_contact_list = ref_doc.get("__onload").get('contact_list') + opportunity_doc_contact_list = [contact for contact in self.get('__onload').get('contact_list') if contact not in ref_doc_contact_list] + ref_doc_contact_list.extend(opportunity_doc_contact_list) + ref_doc.set_onload("contact_list", ref_doc_contact_list) + + ref_doc_addr_list = ref_doc.get("__onload").get('addr_list') + opportunity_doc_addr_list = [addr for addr in self.get('__onload').get('addr_list') if addr not in ref_doc_addr_list] + ref_doc_addr_list.extend(opportunity_doc_addr_list) + ref_doc.set_onload("addr_list", ref_doc_addr_list) + self.set("__onload", ref_doc.get("__onload")) def after_insert(self): From 94f4c92a039ae4039df55eb32bec0739387c6a65 Mon Sep 17 00:00:00 2001 From: Dietmar Fischer Date: Fri, 2 Aug 2024 12:51:48 +0200 Subject: [PATCH 08/44] fix: pre-commit for better code formatting (cherry picked from commit 511a0b9f375bd6f86fd689d35eb6ee395c85a5c6) --- erpnext/crm/doctype/opportunity/opportunity.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 16de1208de5..9229ab22227 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -96,17 +96,23 @@ class Opportunity(TransactionBase, CRMNote): load_address_and_contact(ref_doc) load_address_and_contact(self) - - ref_doc_contact_list = ref_doc.get("__onload").get('contact_list') - opportunity_doc_contact_list = [contact for contact in self.get('__onload').get('contact_list') if contact not in ref_doc_contact_list] + + ref_doc_contact_list = ref_doc.get("__onload").get("contact_list") + opportunity_doc_contact_list = [ + contact + for contact in self.get("__onload").get("contact_list") + if contact not in ref_doc_contact_list + ] ref_doc_contact_list.extend(opportunity_doc_contact_list) ref_doc.set_onload("contact_list", ref_doc_contact_list) - ref_doc_addr_list = ref_doc.get("__onload").get('addr_list') - opportunity_doc_addr_list = [addr for addr in self.get('__onload').get('addr_list') if addr not in ref_doc_addr_list] + ref_doc_addr_list = ref_doc.get("__onload").get("addr_list") + opportunity_doc_addr_list = [ + addr for addr in self.get("__onload").get("addr_list") if addr not in ref_doc_addr_list + ] ref_doc_addr_list.extend(opportunity_doc_addr_list) ref_doc.set_onload("addr_list", ref_doc_addr_list) - + self.set("__onload", ref_doc.get("__onload")) def after_insert(self): From 9789648175e61ea64d9a326c2f4b274067eecbc0 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 7 Aug 2024 20:08:54 +0200 Subject: [PATCH 09/44] fix: german translations for incoterms --- erpnext/translations/de.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index e80569069e7..a21da662e2b 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -8795,13 +8795,13 @@ Warehouse wise Stock Value,Warenwert nach Lager, Ex Works,Ab Werk, Free Carrier,Frei Frachtführer, Free Alongside Ship,Frei Längsseite Schiff, -Free on Board,Frei an Bord, +Free On Board,Frei an Bord, Carriage Paid To,Frachtfrei, Carriage and Insurance Paid to,Frachtfrei versichert, Cost and Freight,Kosten und Fracht, "Cost, Insurance and Freight","Kosten, Versicherung und Fracht", -Delivered at Place,Geliefert benannter Ort, -Delivered at Place Unloaded,Geliefert benannter Ort entladen, +Delivered At Place,Geliefert benannter Ort, +Delivered At Place Unloaded,Geliefert benannter Ort entladen, Delivered Duty Paid,Geliefert verzollt, Discount Validity,Frist für den Rabatt, Discount Validity Based On,Frist für den Rabatt berechnet sich nach, From 7ed7c22469ba8876476214240540237491097b45 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Aug 2024 11:58:58 +0530 Subject: [PATCH 10/44] fix: Maintain same rate on qty change on Quotation to Sales Order (cherry picked from commit 91ce9fce9b20444e62b80dc5abab5624ddab2d72) --- erpnext/public/js/controllers/transaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 8699f253e3d..bc20901ae1c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1253,6 +1253,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "Sales Invoice Item": ["dn_detail", "so_detail", "sales_invoice_item"], "Purchase Receipt Item": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], "Purchase Invoice Item": ["purchase_order_item", "pr_detail", "po_detail"], + "Sales Order Item": ["prevdoc_docname", "quotation_item"], }; const mappped_fields = mapped_item_field_map[item.doctype] || []; From da6eea77437cffce628a08ef2ce5e6da2afdf193 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:05:08 +0200 Subject: [PATCH 11/44] fix(Exchange Rate Revaluation): translatable strings (cherry picked from commit dc29fb8759bed1e6bb3da3cd7e009b51d8d0254b) --- .../exchange_rate_revaluation/exchange_rate_revaluation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index 741039acae6..db3c284ca8d 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -91,12 +91,12 @@ frappe.ui.form.on("Exchange Rate Revaluation", { method: "make_jv_entries", doc: frm.doc, freeze: true, - freeze_message: "Making Journal Entries...", + freeze_message: __("Creating Journal Entries..."), callback: function (r) { if (r.message) { let response = r.message; if (response["revaluation_jv"] || response["zero_balance_jv"]) { - frappe.msgprint(__("Journals have been created")); + frappe.msgprint(__("Journal entries have been created")); } } }, From 13f6f3259d49c317ce465bc01c0b7c7fb46fa1ab Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 8 Aug 2024 12:05:25 +0200 Subject: [PATCH 12/44] refactor(Exchange Rate Revaluation): remove unused variables (cherry picked from commit 8bd84e9d51d21f8e22dd540d734c2c4778a843a2) --- .../exchange_rate_revaluation/exchange_rate_revaluation.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index db3c284ca8d..eeda531c4d6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -85,8 +85,6 @@ frappe.ui.form.on("Exchange Rate Revaluation", { }, make_jv: function (frm) { - let revaluation_journal = null; - let zero_balance_journal = null; frappe.call({ method: "make_jv_entries", doc: frm.doc, From a2df2768802c54001286913ac17ebaf207e40351 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 8 Aug 2024 20:20:22 +0200 Subject: [PATCH 13/44] fix: german translation of exit --- erpnext/translations/de.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index a21da662e2b..12b2854862c 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1334,7 +1334,7 @@ Lead to Quotation,Vom Lead zum Angebot, Learn,Lernen, Leave Management,Urlaube verwalten, Leave and Attendance,Urlaub und Anwesenheit, -Leave application {0} already exists against the student {1},Verlassen der Anwendung {0} ist bereits für den Schüler {1} vorhanden, +Leave application {0} already exists against the student {1},Abwesenheitsantrag {0} existiert bereits für den Schüler {1}, Leaves has been granted sucessfully,Urlaub wurde genehmigt, Leaves must be allocated in multiples of 0.5,"Abwesenheiten müssen ein Vielfaches von 0,5 sein", Ledger,Hauptbuch, @@ -5529,10 +5529,10 @@ Guardian Details,Erziehungsberechtigten-Details, Guardians,Erziehungsberechtigte, Sibling Details,Geschwister-Details, Siblings,Geschwister, -Exit,Verlassen, +Exit,Austritt, Date of Leaving,Austrittsdatum, Leaving Certificate Number,Leaving Certificate Nummer, -Reason For Leaving,Grund für das Verlassen, +Reason For Leaving,Grund für den Austritt, Student Admission,Studenten Eintritt, Admission Start Date,Stichtag zum Zulassungsbeginn, Admission End Date,Stichtag für Zulassungsende, From 64e75a8e089c7b347f255c7c8083d4297c0ea069 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 11:34:05 +0530 Subject: [PATCH 14/44] fix: typeerror on payment entry (cherry picked from commit 9cdca0d66274d23f947c694c06a8be44d4ada2b5) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d9a84431bbd..c28dcf525df 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -230,7 +230,7 @@ frappe.ui.form.on("Payment Entry", { hide_unhide_fields: function (frm) { var company_currency = frm.doc.company - ? frappe.get_doc(":Company", frm.doc.company).default_currency + ? frappe.get_doc(":Company", frm.doc.company)?.default_currency : ""; frm.toggle_display( From d8939e0bb0136ed6947d0f542ae0c91873f57237 Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Mon, 22 Jul 2024 10:11:37 +0200 Subject: [PATCH 15/44] fix: Sort lists before calling itertools.groupby (cherry picked from commit 45a6ecbd38e60c6403ee268e3b865072fa50c2dd) --- .../opportunity_summary_by_sales_stage.py | 4 +++- .../sales_pipeline_analytics/sales_pipeline_analytics.py | 4 +++- erpnext/selling/page/sales_funnel/sales_funnel.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.py | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py index 5a36c999747..2e8ae7e340e 100644 --- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py +++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.py @@ -108,7 +108,9 @@ class OpportunitySummaryBySalesStage: self.grouped_data = [] grouping_key = lambda o: (o["sales_stage"], o[based_on]) # noqa - for (sales_stage, _based_on), rows in groupby(self.query_result, grouping_key): + for (sales_stage, _based_on), rows in groupby( + sorted(self.query_result, key=grouping_key), key=grouping_key + ): self.grouped_data.append( { "sales_stage": sales_stage, diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py index e5d34231a87..9c19e9f0148 100644 --- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py +++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py @@ -122,7 +122,9 @@ class SalesPipelineAnalytics: self.grouped_data = [] grouping_key = lambda o: (o.get(self.pipeline_by) or "Not Assigned", o[self.period_by]) # noqa - for (pipeline_by, period_by), rows in groupby(self.query_result, grouping_key): + for (pipeline_by, period_by), rows in groupby( + sorted(self.query_result, key=grouping_key), grouping_key + ): self.grouped_data.append( { self.pipeline_by: pipeline_by, diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index 24bb0d2fe71..73deb8fe66b 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -93,7 +93,7 @@ def get_opp_by_lead_source(from_date, to_date, company): summary = {} sales_stages = set() group_key = lambda o: (o["source"], o["sales_stage"]) # noqa - for (source, sales_stage), rows in groupby(cp_opportunities, group_key): + for (source, sales_stage), rows in groupby(sorted(cp_opportunities, key=group_key), group_key): summary.setdefault(source, {})[sales_stage] = sum(r["compound_amount"] for r in rows) sales_stages.add(sales_stage) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 066dba95b1e..8e447f65a6f 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -1097,7 +1097,8 @@ def create_delivery_note(source_name, target_doc=None): ) ) - for customer, rows in groupby(sales_orders, key=lambda so: so["customer"]): + group_key = lambda so: so["customer"] # noqa + for customer, rows in groupby(sorted(sales_orders, key=group_key), key=group_key): sales_dict[customer] = {row.sales_order for row in rows} if sales_dict: From 6f16ae3e00d276b95cf805318d70591aecadfb5e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:24:34 +0530 Subject: [PATCH 16/44] fix: delivery note creation issue (backport #42696) (#42697) fix: delivery note creation issue (#42696) (cherry picked from commit b65072cd98ceb7964adf9c09474bedce73ae0d5f) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/delivery_note/delivery_note.py | 3 +++ erpnext/stock/doctype/pick_list/pick_list.py | 1 + 2 files changed, 4 insertions(+) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index c971c8da4f0..8a096aca80c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -328,6 +328,9 @@ class DeliveryNote(SellingController): return for item in self.items: + if item.use_serial_batch_fields: + continue + if item.pick_list_item and not item.serial_and_batch_bundle: filters = { "item_code": item.item_code, diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 8e447f65a6f..8d7022c3581 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -1179,6 +1179,7 @@ def map_pl_locations(pick_list, item_mapper, delivery_note, sales_order=None): dn_item.qty = flt(location.picked_qty) / (flt(location.conversion_factor) or 1) dn_item.batch_no = location.batch_no dn_item.serial_no = location.serial_no + dn_item.use_serial_batch_fields = location.use_serial_batch_fields update_delivery_note_item(source_doc, dn_item, delivery_note) From a99057754d436022555101230b6a53e417fe9e61 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 11 Aug 2024 12:35:13 +0530 Subject: [PATCH 17/44] fix: warning message for negative stock (backport #42683) (#42710) fix: warning message for negative stock (#42683) (cherry picked from commit deccb007c1e6626f2febaf2407bbfc84cdc477bb) Co-authored-by: rohitwaghchaure --- erpnext/stock/stock_ledger.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ac78f1a7f68..693481dfba8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -6,13 +6,14 @@ import gzip import json import frappe -from frappe import _, scrub +from frappe import _, bold, scrub from frappe.model.meta import get_field_precision from frappe.query_builder.functions import Sum from frappe.utils import ( cint, cstr, flt, + format_date, get_link_to_form, getdate, now, @@ -743,9 +744,29 @@ class update_entries_after: return self.distinct_item_warehouses[key].dependent_voucher_detail_nos + def validate_previous_sle_qty(self, sle): + previous_sle = self.data[sle.warehouse].previous_sle + if previous_sle and previous_sle.get("qty_after_transaction") < 0 and sle.get("actual_qty") > 0: + frappe.msgprint( + _( + "The stock for the item {0} in the {1} warehouse was negative on the {2}. You should create a positive entry {3} before the date {4} and time {5} to post the correct valuation rate. For more details, please read the documentation." + ).format( + bold(sle.item_code), + bold(sle.warehouse), + bold(format_date(previous_sle.posting_date)), + sle.voucher_no, + bold(format_date(previous_sle.posting_date)), + bold(previous_sle.posting_time), + ), + title=_("Warning on Negative Stock"), + indicator="blue", + ) + def process_sle(self, sle): # previous sle data for this warehouse self.wh_data = self.data[sle.warehouse] + + self.validate_previous_sle_qty(sle) self.affected_transactions.add((sle.voucher_type, sle.voucher_no)) if (sle.serial_no and not self.via_landed_cost_voucher) or not cint(self.allow_negative_stock): From 50b1fa5deb2a502782cac6884c4ed9f82b1cd335 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 15:20:04 +0530 Subject: [PATCH 18/44] fix: currency changing while making PR from the PO (backport #42718) (#42721) fix: currency changing while making PR from the PO (#42718) (cherry picked from commit 17ba0cff446737908cd9828d4776e35596f05764) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 29 ++++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index bc20901ae1c..4696c7f3b4d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -840,6 +840,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } var get_party_currency = function() { + if (me.is_a_mapped_document()) { + return; + } + var party_type = frappe.meta.has_field(me.frm.doc.doctype, "customer") ? "Customer" : "Supplier"; var party_name = me.frm.doc[party_type.toLowerCase()]; if (party_name) { @@ -1249,17 +1253,24 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe is_a_mapped_document(item) { const mapped_item_field_map = { - "Delivery Note Item": ["si_detail", "so_detail", "dn_detail"], - "Sales Invoice Item": ["dn_detail", "so_detail", "sales_invoice_item"], - "Purchase Receipt Item": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], - "Purchase Invoice Item": ["purchase_order_item", "pr_detail", "po_detail"], - "Sales Order Item": ["prevdoc_docname", "quotation_item"], + "Delivery Note": ["si_detail", "so_detail", "dn_detail"], + "Sales Invoice": ["dn_detail", "so_detail", "sales_invoice_item"], + "Purchase Receipt": ["purchase_order_item", "purchase_invoice_item", "purchase_receipt_item"], + "Purchase Invoice": ["purchase_order_item", "pr_detail", "po_detail"], + "Sales Order": ["prevdoc_docname", "quotation_item"], }; - const mappped_fields = mapped_item_field_map[item.doctype] || []; + const mappped_fields = mapped_item_field_map[this.frm.doc.doctype] || []; - return mappped_fields - .map((field) => item[field]) - .filter(Boolean).length > 0; + if (item) { + return mappped_fields + .map((field) => item[field]) + .filter(Boolean).length > 0; + } else if (this.frm.doc?.items) { + let first_row = this.frm.doc.items[0]; + let mapped_rows = mappped_fields.filter(d => first_row[d]) + + return mapped_rows?.length > 0; + } } batch_no(doc, cdt, cdn) { From c71f06be9e8ad793a3d57cef7598415220e67925 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Aug 2024 10:37:57 +0530 Subject: [PATCH 19/44] fix: update 'Paid Amount' on forex payment request (cherry picked from commit 7b0dfb2a0561b30aaa45427697ac3c5b5b424576) --- .../doctype/payment_request/payment_request.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 48bb6f2ae86..a2c6a9d856a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -338,6 +338,17 @@ class PaymentRequest(Document): payment_entry.received_amount = amount payment_entry.get("references")[0].allocated_amount = amount + # Update 'Paid Amount' on Forex transactions + if self.currency != ref_doc.company_currency: + if ( + self.payment_request_type == "Outward" + and payment_entry.paid_from_account_currency == ref_doc.company_currency + and payment_entry.paid_from_account_currency != payment_entry.paid_to_account_currency + ): + payment_entry.paid_amount = payment_entry.base_paid_amount = ( + payment_entry.target_exchange_rate * payment_entry.received_amount + ) + for dimension in get_accounting_dimensions(): payment_entry.update({dimension: self.get(dimension)}) From 47bf2d408b78c4448563178a2f95148bb03c2dba Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 16:10:05 +0530 Subject: [PATCH 20/44] test: make use of test fixture (cherry picked from commit d6d0a1b38d7952d6ca289e9b3320e67aaa1c2f3c) # Conflicts: # erpnext/accounts/doctype/payment_request/test_payment_request.py --- .../payment_request/test_payment_request.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 932060895b0..8ce56740dcf 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -4,6 +4,7 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice @@ -32,7 +33,7 @@ payment_method = [ ] -class TestPaymentRequest(unittest.TestCase): +class TestPaymentRequest(FrappeTestCase): def setUp(self): if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"): frappe.get_doc(payment_gateway).insert(ignore_permissions=True) @@ -45,6 +46,31 @@ class TestPaymentRequest(unittest.TestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) +<<<<<<< HEAD +======= + send_email = patch( + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", + return_value=None, + ) + self.send_email = send_email.start() + self.addCleanup(send_email.stop) + get_payment_url = patch( + # this also shadows one (1) call to _get_payment_gateway_controller + "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", + return_value=PAYMENT_URL, + ) + self.get_payment_url = get_payment_url.start() + self.addCleanup(get_payment_url.stop) + _get_payment_gateway_controller = patch( + "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", + ) + self._get_payment_gateway_controller = _get_payment_gateway_controller.start() + self.addCleanup(_get_payment_gateway_controller.stop) + + def tearDown(self): + frappe.db.rollback() + +>>>>>>> d6d0a1b38d (test: make use of test fixture) def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From 44745a757d51d3c10e5e7574ae27cabdbdd442ad Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Aug 2024 17:53:43 +0530 Subject: [PATCH 21/44] test: currency conversion on foreign currency account (cherry picked from commit f913c0fde1899fb8e871f1488f77fc8b8ab0218b) --- .../payment_request/test_payment_request.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 8ce56740dcf..059edabb08d 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -9,6 +9,7 @@ from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice +from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.setup.utils import get_exchange_rate @@ -286,3 +287,19 @@ class TestPaymentRequest(FrappeTestCase): # Try to make Payment Request more than SO amount, should give validation pr2.grand_total = 900 self.assertRaises(frappe.ValidationError, pr2.save) + + def test_conversion_on_foreign_currency_accounts(self): + po_doc = create_purchase_order(supplier="_Test Supplier USD", currency="USD", do_not_submit=1) + po_doc.conversion_rate = 80 + po_doc.items[0].qty = 1 + po_doc.items[0].rate = 10 + po_doc.save().submit() + + pr = make_payment_request(dt=po_doc.doctype, dn=po_doc.name, recipient_id="nabin@erpnext.com") + pr = frappe.get_doc(pr).save().submit() + + pe = pr.create_payment_entry() + self.assertEqual(pe.base_paid_amount, 800) + self.assertEqual(pe.paid_amount, 800) + self.assertEqual(pe.base_received_amount, 800) + self.assertEqual(pe.received_amount, 10) From b41f10c1b98b01a181a6f9dbdf2531b108dc3bae Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 16:13:59 +0530 Subject: [PATCH 22/44] chore: resolve conflict --- .../payment_request/test_payment_request.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 059edabb08d..6d15f84d7cf 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -47,31 +47,6 @@ class TestPaymentRequest(FrappeTestCase): ): frappe.get_doc(method).insert(ignore_permissions=True) -<<<<<<< HEAD -======= - send_email = patch( - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email", - return_value=None, - ) - self.send_email = send_email.start() - self.addCleanup(send_email.stop) - get_payment_url = patch( - # this also shadows one (1) call to _get_payment_gateway_controller - "erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url", - return_value=PAYMENT_URL, - ) - self.get_payment_url = get_payment_url.start() - self.addCleanup(get_payment_url.stop) - _get_payment_gateway_controller = patch( - "erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller", - ) - self._get_payment_gateway_controller = _get_payment_gateway_controller.start() - self.addCleanup(_get_payment_gateway_controller.stop) - - def tearDown(self): - frappe.db.rollback() - ->>>>>>> d6d0a1b38d (test: make use of test fixture) def test_payment_request_linkings(self): so_inr = make_sales_order(currency="INR", do_not_save=True) so_inr.disable_rounded_total = 1 From a429f2f626fdae163b7661935dd28f42ed68ce24 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 13 Aug 2024 01:48:36 +0530 Subject: [PATCH 23/44] fix: cancel Journal Entry on cancellation of asset value adjustment (cherry picked from commit 4b7f1f2d67158d6c2c201ba3d22fe40a24884210) --- .../doctype/asset_value_adjustment/asset_value_adjustment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 0d02347f20a..8dfed9cba8a 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -56,6 +56,7 @@ class AssetValueAdjustment(Document): ) def on_cancel(self): + frappe.get_doc("Journal Entry", self.journal_entry).cancel() self.update_asset(self.current_asset_value) add_asset_activity( self.asset, From 774db567621e3b0b7393f85faa93686e7be950b9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:37:55 +0530 Subject: [PATCH 24/44] refactor: combine vouchers from both ignore (cherry picked from commit 4cc5cd5a71fbc434ca7e95b24b9b7a491840854a) --- erpnext/accounts/report/general_ledger/general_ledger.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 63e57438fca..2564eb0800f 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -248,7 +248,10 @@ def get_conditions(filters): as_list=True, ) if system_generated_cr_dr_journals: - filters.update({"voucher_no_not_in": [x[0] for x in system_generated_cr_dr_journals]}) + vouchers_to_ignore = (filters.get("voucher_no_not_in") or []) + [ + x[0] for x in system_generated_cr_dr_journals + ] + filters.update({"voucher_no_not_in": vouchers_to_ignore}) if filters.get("voucher_no_not_in"): conditions.append("voucher_no not in %(voucher_no_not_in)s") From 19dfeca96d1d623acfb04bde765027f337ebadbf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:41:31 +0530 Subject: [PATCH 25/44] refactor: cr and dr note filter in Statement of Accounts (cherry picked from commit 0cf478c4c26f61b1e23195a27468f69cbb21b7b4) # Conflicts: # erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json --- .../process_statement_of_accounts.json | 11 +++++++++++ .../process_statement_of_accounts.py | 1 + 2 files changed, 12 insertions(+) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index ae6059c0057..545ea22d799 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -16,6 +16,7 @@ "cost_center", "territory", "ignore_exchange_rate_revaluation_journals", + "ignore_cr_dr_notes", "column_break_14", "to_date", "finance_book", @@ -383,10 +384,20 @@ "fieldname": "ignore_exchange_rate_revaluation_journals", "fieldtype": "Check", "label": "Ignore Exchange Rate Revaluation Journals" + }, + { + "default": "0", + "fieldname": "ignore_cr_dr_notes", + "fieldtype": "Check", + "label": "Ignore System Generated Credit / Debit Notes" } ], "links": [], +<<<<<<< HEAD "modified": "2023-12-18 12:20:08.965120", +======= + "modified": "2024-08-13 10:41:18.381165", +>>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index e0ec144e314..43951ac5046 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -54,6 +54,7 @@ class ProcessStatementOfAccounts(Document): frequency: DF.Literal["Weekly", "Monthly", "Quarterly"] from_date: DF.Date | None group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"] + ignore_cr_dr_notes: DF.Check ignore_exchange_rate_revaluation_journals: DF.Check include_ageing: DF.Check include_break: DF.Check From f3401243be08d70150a257e2004eca5df0f5334c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 10:42:32 +0530 Subject: [PATCH 26/44] refactor: pass filter to General Ledger (cherry picked from commit 90880c8c01642cd40782f51c08d30d0896ac066e) --- .../process_statement_of_accounts.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 43951ac5046..509199ccae6 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -134,6 +134,9 @@ def get_statement_dict(doc, get_statement_dict=False): if doc.ignore_exchange_rate_revaluation_journals: filters.update({"ignore_err": True}) + if doc.ignore_cr_dr_notes: + filters.update({"ignore_cr_dr_notes": True}) + if doc.report == "General Ledger": filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency)) col, res = get_soa(filters) From 70d5593aceca5a6aa5f86cab985f86d0bd0f02cd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 11:21:09 +0530 Subject: [PATCH 27/44] chore: resolve conflict --- .../process_statement_of_accounts.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index 545ea22d799..22be5299280 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -393,11 +393,7 @@ } ], "links": [], -<<<<<<< HEAD - "modified": "2023-12-18 12:20:08.965120", -======= "modified": "2024-08-13 10:41:18.381165", ->>>>>>> 0cf478c4c2 (refactor: cr and dr note filter in Statement of Accounts) "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", From 20c1bcd654ea6e6e795546379355b17758bb86b5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 2 Aug 2024 11:50:57 +0530 Subject: [PATCH 28/44] fix: error message in coa importer (cherry picked from commit 5d0a38dfc76b0a8874f21902636e8bfb5c3bf514) --- .../chart_of_accounts_importer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index e24c7c9409f..e7dab34d04a 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -47,9 +47,11 @@ def validate_columns(data): no_of_columns = max([len(d) for d in data]) - if no_of_columns > 8: + if no_of_columns != 8: frappe.throw( - _("More columns found than expected. Please compare the uploaded file with standard template"), + _( + "Columns are not according to template. Please compare the uploaded file with standard template" + ), title=(_("Wrong Template")), ) From a8de8aecf5a2e85bab16ae9a52b7c2bfe7038a4a Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 12 Aug 2024 17:09:39 +0530 Subject: [PATCH 29/44] fix: text color in sales funnel report based on theme (cherry picked from commit 61bc0925d52291d87ad73b72ffb8b59b6360d0b7) --- erpnext/selling/page/sales_funnel/sales_funnel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 1e2aa8ea56b..d9a0be9eedd 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -248,7 +248,7 @@ erpnext.SalesFunnel = class SalesFunnel { context.fill(); // draw text - context.fillStyle = "black"; + context.fillStyle = getComputedStyle(document.body).getPropertyValue("--text-color"); context.textBaseline = "middle"; context.font = "1.1em sans-serif"; context.fillText(__(title), width + 20, y_mid); From 765c1104c4c4a76a2e65914b6e9fbf58e32ecb09 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 9 Aug 2024 03:21:09 +0530 Subject: [PATCH 30/44] fix: fetch months last date to avoid miscalculation (cherry picked from commit 70ff4e76446d6996cfcce3b07e9653167f1c1237) --- erpnext/assets/doctype/asset/test_asset.py | 4 +-- .../asset_depreciation_schedule.py | 27 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 586385adb0d..06f170200a8 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -234,7 +234,7 @@ class TestAsset(AssetSetup): pro_rata_amount, _, _ = _get_pro_rata_amt( asset.finance_books[0], 9000, - get_last_day(add_months(purchase_date, 1)), + add_days(get_last_day(add_months(purchase_date, 1)), 1), date, original_schedule_date=get_last_day(nowdate()), ) @@ -320,7 +320,7 @@ class TestAsset(AssetSetup): pro_rata_amount, _, _ = _get_pro_rata_amt( asset.finance_books[0], 9000, - get_last_day(add_months(purchase_date, 1)), + add_days(get_last_day(add_months(purchase_date, 1)), 1), date, original_schedule_date=get_last_day(nowdate()), ) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index e7b5a25cc73..d7dcfac3aab 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -291,7 +291,9 @@ class AssetDepreciationSchedule(Document): if skip_row: continue - schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) + schedule_date = get_last_day( + add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) + ) if not current_fiscal_year_end_date: current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2] elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): @@ -330,8 +332,10 @@ class AssetDepreciationSchedule(Document): getdate(asset_doc.available_for_use_date), (asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation), ) + if is_last_day_of_the_month(getdate(asset_doc.available_for_use_date)): + from_date = get_last_day(from_date) if self.depreciation_schedule: - from_date = self.depreciation_schedule[-1].schedule_date + from_date = add_days(self.depreciation_schedule[-1].schedule_date, 1) depreciation_amount, days, months = _get_pro_rata_amt( row, @@ -353,9 +357,8 @@ class AssetDepreciationSchedule(Document): and not self.opening_accumulated_depreciation and not self.flags.wdv_it_act_applied ): - from_date = add_days( - asset_doc.available_for_use_date, -1 - ) # needed to calc depr amount for available_for_use_date too + from_date = asset_doc.available_for_use_date + # needed to calc depr amount for available_for_use_date too depreciation_amount, days, months = _get_pro_rata_amt( row, depreciation_amount, @@ -406,6 +409,8 @@ class AssetDepreciationSchedule(Document): (n + self.opening_number_of_booked_depreciations) * cint(row.frequency_of_depreciation), ) + if is_last_day_of_the_month(getdate(asset_doc.available_for_use_date)): + asset_doc.to_date = get_last_day(asset_doc.to_date) depreciation_amount_without_pro_rata = depreciation_amount @@ -421,7 +426,7 @@ class AssetDepreciationSchedule(Document): depreciation_amount_without_pro_rata, depreciation_amount ) - schedule_date = add_days(schedule_date, days) + schedule_date = add_days(schedule_date, days - 1) if not depreciation_amount: continue @@ -553,9 +558,11 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False): # otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 # from_date = 01/01/2022 if row.depreciation_method in ("Straight Line", "Manual"): - prev_depreciation_start_date = add_months( - row.depreciation_start_date, - (row.frequency_of_depreciation * -1) * asset_doc.opening_number_of_booked_depreciations, + prev_depreciation_start_date = get_last_day( + add_months( + row.depreciation_start_date, + (row.frequency_of_depreciation * -1) * asset_doc.opening_number_of_booked_depreciations, + ) ) from_date = asset_doc.available_for_use_date days = date_diff(prev_depreciation_start_date, from_date) + 1 @@ -610,7 +617,7 @@ def _get_pro_rata_amt( has_wdv_or_dd_non_yearly_pro_rata=False, original_schedule_date=None, ): - days = date_diff(to_date, from_date) + days = date_diff(to_date, from_date) + 1 months = month_diff(to_date, from_date) if has_wdv_or_dd_non_yearly_pro_rata: total_days = get_total_days(original_schedule_date or to_date, 12) From 582fffca931eca7754e24eee61d52c1d68ef8a4c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 17:37:48 +0530 Subject: [PATCH 31/44] fix: force fetch updates for subcription (cherry picked from commit 1ef890db732caff9fc77716fd3932ae9c4055b4f) --- .../doctype/subscription/subscription.js | 13 +++++++++++++ .../doctype/subscription/subscription.py | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index b3e21abcaae..306b049220d 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -38,6 +38,12 @@ frappe.ui.form.on("Subscription", { __("Actions") ); + frm.add_custom_button( + __("Force Fetch Subscription Updates"), + () => frm.trigger("force_fetch_subscription_updates"), + __("Actions") + ); + frm.add_custom_button( __("Cancel Subscription"), () => frm.trigger("cancel_this_subscription"), @@ -82,4 +88,11 @@ frappe.ui.form.on("Subscription", { } }); }, + force_fetch_subscription_updates: function (frm) { + frm.call("force_fetch_subscription_updates").then((r) => { + if (!r.exec) { + frm.reload_doc(); + } + }); + }, }); diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index a9dbf333335..6968be8e381 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -717,6 +717,25 @@ class Subscription(Document): self.update_subscription_period(posting_date or nowdate()) self.save() + @frappe.whitelist() + def force_fetch_subscription_updates(self): + """ + Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end + It makes use of 'Proces Subscription' to force processing in a specific 'posting_date' + """ + processing_date = None + if self.generate_invoice_at == "Beginning of the current subscription period": + processing_date = self.current_invoice_start + elif self.generate_invoice_at == "End of the current subscription period": + processing_date = self.current_invoice_end + elif self.generate_invoice_at == "Days before the current subscription period": + processing_date = add_days(self.current_invoice_start, -self.number_of_days) + + process_subscription = frappe.new_doc("Process Subscription") + process_subscription.posting_date = processing_date + process_subscription.subscription = self.name + process_subscription.save().submit() + def is_prorate() -> int: return cint(frappe.db.get_single_value("Subscription Settings", "prorate")) From 1f1e93467586f5116c786fe453ddd309602cf33b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 12:34:10 +0530 Subject: [PATCH 32/44] refactor: don't process future subscriptions (cherry picked from commit 3a115774114a1986435739936c195a17a0b65af3) --- erpnext/accounts/doctype/subscription/subscription.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 6968be8e381..916757a8d6d 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -723,6 +723,12 @@ class Subscription(Document): Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end It makes use of 'Proces Subscription' to force processing in a specific 'posting_date' """ + + # Don't process future subscriptions + if nowdate() < self.current_invoice_start: + frappe.msgprint(_("Subscription for Future dates cannot be processed.")) + return + processing_date = None if self.generate_invoice_at == "Beginning of the current subscription period": processing_date = self.current_invoice_start From ab59b3360675858738d2b22336acb0e75101d991 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 12:35:16 +0530 Subject: [PATCH 33/44] chore: minor naming change (cherry picked from commit d8b6767697afe6e59b33e62466ffcb8653b23b96) --- erpnext/accounts/doctype/subscription/subscription.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 306b049220d..629d118080a 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -39,7 +39,7 @@ frappe.ui.form.on("Subscription", { ); frm.add_custom_button( - __("Force Fetch Subscription Updates"), + __("Force-Fetch Subscription Updates"), () => frm.trigger("force_fetch_subscription_updates"), __("Actions") ); From a1f98603a60347b4da2713f9d23f3b223e406564 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 13 Aug 2024 16:05:06 +0530 Subject: [PATCH 34/44] refactor: test for force-fetch on future subscription (cherry picked from commit fd680380bbb93313a0d0853eaf9144726c44551c) --- .../doctype/subscription/test_subscription.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index af3916ae469..8d4ec3dd084 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -521,6 +521,18 @@ class TestSubscription(FrappeTestCase): subscription.process(posting_date="2023-01-22") self.assertEqual(len(subscription.invoices), 2) + def test_future_subscription(self): + """Force-Fetch should not process future subscriptions""" + subscription = create_subscription( + start_date=add_months(nowdate(), 1), + submit_invoice=0, + generate_new_invoices_past_due_date=1, + party="_Test Subscription Customer John Doe", + ) + subscription.force_fetch_subscription_updates() + subscription.reload() + self.assertEqual(len(subscription.invoices), 0) + def make_plans(): create_plan(plan_name="_Test Plan Name", cost=900, currency="INR") From 536dc47eb062f7d512b0909a920f09592e528e49 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:09:27 +0530 Subject: [PATCH 35/44] fix: price list when invoice created from timesheet (cherry picked from commit 39d6df7c7d225cb946a640e980fcc5bafb1c6a2d) --- erpnext/projects/doctype/timesheet/timesheet.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 5b9b5c9a9f6..5cd4e68b17f 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -379,6 +379,9 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None target.project = timesheet.parent_project if customer: target.customer = customer + customer_doc = frappe.get_doc("Customer", customer) + if customer_doc and customer_doc.default_price_list: + target.selling_price_list = customer_doc.default_price_list if currency: target.currency = currency From 2926915a06b13e2a29b07cad589ad0f2677b54e2 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Wed, 29 May 2024 10:26:49 +0530 Subject: [PATCH 36/44] fix: price list when invoice created from timesheet (cherry picked from commit 882227a460ae33e0829c0876f5ad8d33f4ded639) --- erpnext/projects/doctype/timesheet/timesheet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 5cd4e68b17f..fb15b507efb 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -379,9 +379,9 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None target.project = timesheet.parent_project if customer: target.customer = customer - customer_doc = frappe.get_doc("Customer", customer) - if customer_doc and customer_doc.default_price_list: - target.selling_price_list = customer_doc.default_price_list + default_price_list = frappe.get_value("Customer", customer, "default_price_list") + if default_price_list: + target.selling_price_list = default_price_list if currency: target.currency = currency From 8624a0abce26576616783b7ce4d43f912bc80d68 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 20:31:05 +0530 Subject: [PATCH 37/44] fix: duplicate labels in stock entry (backport #42756) (#42758) fix: duplicate labels in stock entry (#42756) (cherry picked from commit 8aadc18ee8f8921007f71ad3fee1fda2fb2f3ac0) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 495af7f173a..3f467d3627a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -303,7 +303,7 @@ "depends_on": "from_warehouse", "fieldname": "source_warehouse_address", "fieldtype": "Link", - "label": "Source Warehouse Address", + "label": "Source Warehouse Address Link", "options": "Address" }, { @@ -333,7 +333,7 @@ "depends_on": "to_warehouse", "fieldname": "target_warehouse_address", "fieldtype": "Link", - "label": "Target Warehouse Address", + "label": "Target Warehouse Address Link", "options": "Address" }, { @@ -686,10 +686,10 @@ "read_only": 1 }, { - "fieldname": "tab_connections", - "fieldtype": "Tab Break", - "label": "Connections", - "show_dashboard": 1 + "fieldname": "tab_connections", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "icon": "fa fa-file-text", @@ -697,7 +697,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-06-26 19:12:17.937088", + "modified": "2024-08-13 19:02:42.386955", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", From defd5541b060dd901171fcb2ffc0029c75931b26 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 14 Aug 2024 00:03:35 +0530 Subject: [PATCH 38/44] fix: resolved conflict (cherry picked from commit c085b6159b3a122852f403aad22ae35999f82a09) --- .../asset_depreciation_schedule.py | 10 ++++++++-- erpnext/assets/doctype/asset_repair/asset_repair.js | 9 +++++++++ erpnext/assets/doctype/asset_repair/asset_repair.py | 8 ++++++-- .../asset_value_adjustment/asset_value_adjustment.py | 10 +++++++--- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index d7dcfac3aab..bad89e93259 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -509,7 +509,10 @@ class AssetDepreciationSchedule(Document): continue if not accumulated_depreciation: - if i > 0 and asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment: + if i > 0 and ( + asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment + or asset_doc.flags.increase_in_asset_value_due_to_repair + ): accumulated_depreciation = self.get("depreciation_schedule")[ i - 1 ].accumulated_depreciation_amount @@ -677,7 +680,7 @@ def get_straight_line_or_manual_depr_amount( # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value elif asset.flags.increase_in_asset_value_due_to_repair: return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt( - row.total_number_of_depreciations + number_of_pending_depreciations ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: @@ -1041,6 +1044,7 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones( date_of_return=None, value_after_depreciation=None, ignore_booked_entry=False, + difference_amount=None, ): for row in asset_doc.get("finance_books"): current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( @@ -1055,6 +1059,8 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones( ) new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) + if asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment and not value_after_depreciation: + value_after_depreciation = row.value_after_depreciation + difference_amount if asset_doc.flags.increase_in_asset_value_due_to_repair and row.depreciation_method in ( "Written Down Value", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 9284c86bd92..67ce6e6f7ef 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -29,6 +29,15 @@ frappe.ui.form.on("Asset Repair", { }; }); + frm.set_query("purchase_invoice", function () { + return { + filters: { + company: frm.doc.company, + docstatus: 1, + }, + }; + }); + frm.set_query("warehouse", "stock_items", function () { return { filters: { diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 903e68e32e0..4e73148828d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -117,7 +117,9 @@ class AssetRepair(AccountsController): get_link_to_form(self.doctype, self.name), ) self.asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) + make_new_active_asset_depr_schedules_and_cancel_current_ones( + self.asset_doc, notes, ignore_booked_entry=True + ) self.asset_doc.save() add_asset_activity( @@ -154,7 +156,9 @@ class AssetRepair(AccountsController): get_link_to_form(self.doctype, self.name), ) self.asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) + make_new_active_asset_depr_schedules_and_cancel_current_ones( + self.asset_doc, notes, ignore_booked_entry=True + ) self.asset_doc.save() add_asset_activity( diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 8dfed9cba8a..9b0212b037f 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -57,7 +57,7 @@ class AssetValueAdjustment(Document): def on_cancel(self): frappe.get_doc("Journal Entry", self.journal_entry).cancel() - self.update_asset(self.current_asset_value) + self.update_asset() add_asset_activity( self.asset, _("Asset's value adjusted after cancellation of Asset Value Adjustment {0}").format( @@ -145,7 +145,7 @@ class AssetValueAdjustment(Document): self.db_set("journal_entry", je.name) - def update_asset(self, asset_value): + def update_asset(self, asset_value=None): asset = frappe.get_doc("Asset", self.asset) if not asset.calculate_depreciation: @@ -171,7 +171,11 @@ class AssetValueAdjustment(Document): ) make_new_active_asset_depr_schedules_and_cancel_current_ones( - asset, notes, value_after_depreciation=asset_value, ignore_booked_entry=True + asset, + notes, + value_after_depreciation=asset_value, + ignore_booked_entry=True, + difference_amount=self.difference_amount, ) asset.flags.ignore_validate_update_after_submit = True asset.save() From d890d02b5cc3c2af584d350f37e211f3cc0ce61c Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:41:53 +0530 Subject: [PATCH 39/44] test: new depreciation after cancelling asset repair (cherry picked from commit 88a5824e31d6fbf017666823f749ae098d0de794) --- .../test_asset_value_adjustment.py | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py index 634ed413773..963be704524 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py @@ -12,6 +12,7 @@ from erpnext.assets.doctype.asset.test_asset import create_asset_data from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( get_asset_depr_schedule_doc, ) +from erpnext.assets.doctype.asset_repair.test_asset_repair import create_asset_repair from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt @@ -128,6 +129,136 @@ class TestAssetValueAdjustment(unittest.TestCase): self.assertEqual(schedules, expected_schedules) + def test_depreciation_after_cancelling_asset_repair(self): + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=120000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") + asset_doc = frappe.get_doc("Asset", asset_name) + asset_doc.calculate_depreciation = 1 + asset_doc.available_for_use_date = "2023-01-15" + asset_doc.purchase_date = "2023-01-15" + + asset_doc.append( + "finance_books", + { + "expected_value_after_useful_life": 200, + "depreciation_method": "Straight Line", + "total_number_of_depreciations": 12, + "frequency_of_depreciation": 1, + "depreciation_start_date": "2023-01-31", + }, + ) + asset_doc.submit() + + post_depreciation_entries(getdate("2023-08-21")) + + # create asset repair + asset_repair = create_asset_repair(asset=asset_doc, capitalize_repair_cost=1, submit=1) + + first_asset_depr_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Active") + + # create asset value adjustment + current_value = get_asset_value_after_depreciation(asset_doc.name) + + adj_doc = make_asset_value_adjustment( + asset=asset_doc.name, + current_asset_value=current_value, + new_asset_value=50000.0, + date="2023-08-21", + ) + adj_doc.submit() + + first_asset_depr_schedule.load_from_db() + + second_asset_depr_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active") + self.assertEqual(second_asset_depr_schedule.status, "Active") + self.assertEqual(first_asset_depr_schedule.status, "Cancelled") + + # Test gl entry creted from asset value adjustemnet + expected_gle = ( + ("_Test Accumulated Depreciations - _TC", 0.0, 5625.29), + ("_Test Depreciations - _TC", 5625.29, 0.0), + ) + + gle = frappe.db.sql( + """select account, debit, credit from `tabGL Entry` + where voucher_type='Journal Entry' and voucher_no = %s + order by account""", + adj_doc.journal_entry, + ) + + self.assertSequenceEqual(gle, expected_gle) + + # test depreciation schedule after asset repair and asset value adjustemnet + expected_schedules = [ + ["2023-01-31", 5474.73, 5474.73], + ["2023-02-28", 9983.33, 15458.06], + ["2023-03-31", 9983.33, 25441.39], + ["2023-04-30", 9983.33, 35424.72], + ["2023-05-31", 9983.33, 45408.05], + ["2023-06-30", 9983.33, 55391.38], + ["2023-07-31", 9983.33, 65374.71], + ["2023-08-31", 2766.67, 68141.38], + ["2023-09-30", 2766.67, 70908.05], + ["2023-10-31", 2766.67, 73674.72], + ["2023-11-30", 2766.67, 76441.39], + ["2023-12-31", 2766.67, 79208.06], + ["2024-01-31", 2766.67, 81974.73], + ["2024-02-29", 2766.67, 84741.4], + ["2024-03-31", 2766.67, 87508.07], + ["2024-04-30", 2766.67, 90274.74], + ["2024-05-31", 2766.67, 93041.41], + ["2024-06-30", 2766.67, 95808.08], + ["2024-07-31", 2766.67, 98574.75], + ["2024-08-31", 2766.67, 101341.42], + ["2024-09-30", 2766.67, 104108.09], + ["2024-10-31", 2766.67, 106874.76], + ["2024-11-30", 2766.67, 109641.43], + ["2024-12-31", 2766.67, 112408.1], + ["2025-01-15", 2766.61, 115174.71], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in second_asset_depr_schedule.get("depreciation_schedule") + ] + + self.assertEqual(schedules, expected_schedules) + + # Cancel asset repair + asset_repair.cancel() + asset_repair.load_from_db() + second_asset_depr_schedule.load_from_db() + + third_asset_depr_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active") + self.assertEqual(third_asset_depr_schedule.status, "Active") + self.assertEqual(second_asset_depr_schedule.status, "Cancelled") + + # After cancelling asset repair asset life will be decreased and new depreciation schedule should be calculated + expected_schedules = [ + ["2023-01-31", 5474.73, 5474.73], + ["2023-02-28", 9983.33, 15458.06], + ["2023-03-31", 9983.33, 25441.39], + ["2023-04-30", 9983.33, 35424.72], + ["2023-05-31", 9983.33, 45408.05], + ["2023-06-30", 9983.33, 55391.38], + ["2023-07-31", 9983.33, 65374.71], + ["2023-08-31", 8133.33, 73508.04], + ["2023-09-30", 8133.33, 81641.37], + ["2023-10-31", 8133.33, 89774.7], + ["2023-11-30", 8133.33, 97908.03], + ["2023-12-31", 8133.33, 106041.36], + ["2024-01-15", 8133.35, 114174.71], + ] + + schedules = [ + [cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in third_asset_depr_schedule.get("depreciation_schedule") + ] + + self.assertEqual(schedules, expected_schedules) + def make_asset_value_adjustment(**args): args = frappe._dict(args) From 8cd1952da3d07dc741aff6f3608480f9101f1e15 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 11:29:39 +0530 Subject: [PATCH 40/44] fix: ledger entries for pos return with update outstanding for self (cherry picked from commit 2cd9b28e5bbf376a8d57f61486e37f1fe88d068a) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a0eef4a4f3e..5d6c8a353fb 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1479,6 +1479,10 @@ class SalesInvoice(SellingController): if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: payment_mode.base_amount -= flt(self.change_amount) + against_voucher = self.name + if self.is_return and self.return_against and not self.update_outstanding_for_self: + against_voucher = self.return_against + if payment_mode.base_amount: # POS, make payment entries gl_entries.append( @@ -1492,7 +1496,7 @@ class SalesInvoice(SellingController): "credit_in_account_currency": payment_mode.base_amount if self.party_account_currency == self.company_currency else payment_mode.amount, - "against_voucher": self.name, + "against_voucher": against_voucher, "against_voucher_type": self.doctype, "cost_center": self.cost_center, }, From 389227bce8e62d25e0dc97a51885df1674e56b54 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 13:15:57 +0530 Subject: [PATCH 41/44] fix: patch to fix incorrect against_voucher references in ledger (cherry picked from commit 487d0a55f551a9b92ad662eed9ba94521e97766e) --- .../v15_0/update_pos_return_ledger_entries.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 erpnext/patches/v15_0/update_pos_return_ledger_entries.py diff --git a/erpnext/patches/v15_0/update_pos_return_ledger_entries.py b/erpnext/patches/v15_0/update_pos_return_ledger_entries.py new file mode 100644 index 00000000000..60e867d1bcb --- /dev/null +++ b/erpnext/patches/v15_0/update_pos_return_ledger_entries.py @@ -0,0 +1,61 @@ +import frappe +from frappe import qb + + +def execute(): + sinv = qb.DocType("Sales Invoice") + pos_returns_without_self = ( + qb.from_(sinv) + .select(sinv.name) + .where( + sinv.docstatus.eq(1) + & sinv.is_pos.eq(1) + & sinv.is_return.eq(1) + & sinv.return_against.notnull() + & sinv.update_outstanding_for_self.eq(0) + ) + .run() + ) + if pos_returns_without_self: + pos_returns_without_self = [x[0] for x in pos_returns_without_self] + + gle = qb.DocType("GL Entry") + gl_against_references = ( + qb.from_(gle) + .select(gle.voucher_no, gle.against_voucher) + .where( + gle.voucher_no.isin(pos_returns_without_self) + & gle.against_voucher.notnull() + & gle.against_voucher.eq(gle.voucher_no) + & gle.is_cancelled.eq(0) + ) + .run() + ) + + _vouchers = list(set([x[0] for x in gl_against_references])) + invoice_return_against = ( + qb.from_(sinv) + .select(sinv.name, sinv.return_against) + .where(sinv.name.isin(_vouchers) & sinv.return_against.notnull()) + .orderby(sinv.name) + .run() + ) + + valid_references = set(invoice_return_against) + actual_references = set(gl_against_references) + + invalid_references = actual_references.difference(valid_references) + + if invalid_references: + # Repost Accounting Ledger + pos_for_reposting = ( + qb.from_(sinv) + .select(sinv.company, sinv.name) + .where(sinv.name.isin([x[0] for x in invalid_references])) + .run(as_dict=True) + ) + for x in pos_for_reposting: + ral = frappe.new_doc("Repost Accounting Ledger") + ral.company = x.company + ral.append("vouchers", {"voucher_type": "Sales Invoice", "voucher_no": x.name}) + ral.save().submit() From 2af48e40a1bb504f334506b9287aed5f0651fd55 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 12 Aug 2024 13:58:04 +0530 Subject: [PATCH 42/44] refactor: update patches.txt (cherry picked from commit 4dc0d3a003720f6bd1f6d87e5a3540d4a03a2fa9) --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e1c2878e9bd..e426a47bb7d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,6 +357,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request +erpnext.patches.v15_0.update_pos_return_ledger_entries # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 From f782af8ab3767d712c87f3e855734b9a59abe36c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Aug 2024 11:32:26 +0530 Subject: [PATCH 43/44] test: against_voucher for pos_returns without updating for self (cherry picked from commit 3fb08583218fe9890ab2703292a327fea047ee97) --- .../sales_invoice/test_sales_invoice.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4e51942ed93..fef30bdfecd 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5,6 +5,7 @@ import copy import json import frappe +from frappe import qb from frappe.model.dynamic_links import get_dynamic_link_map from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, flt, getdate, nowdate, today @@ -3836,6 +3837,40 @@ class TestSalesInvoice(FrappeTestCase): ] self.assertEqual(expected, actual) + def test_pos_returns_without_update_outstanding_for_self(self): + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return + + pos_profile = make_pos_profile() + pos_profile.payments = [] + pos_profile.append("payments", {"default": 1, "mode_of_payment": "Cash"}) + pos_profile.save() + + pos = create_sales_invoice(qty=10, do_not_save=True) + pos.is_pos = 1 + pos.pos_profile = pos_profile.name + pos.append( + "payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500} + ) + pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500}) + pos.save().submit() + + pos_return = make_sales_return(pos.name) + pos_return.update_outstanding_for_self = False + pos_return.save().submit() + + gle = qb.DocType("GL Entry") + res = ( + qb.from_(gle) + .select(gle.against_voucher) + .distinct() + .where( + gle.is_cancelled.eq(0) & gle.voucher_no.eq(pos_return.name) & gle.against_voucher.notnull() + ) + .run(as_list=1) + ) + self.assertEqual(len(res), 1) + self.assertEqual(res[0][0], pos_return.return_against) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( From 3699b96adf22f85d2240ac95c267f40d69ab994a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 14 Aug 2024 11:35:05 +0530 Subject: [PATCH 44/44] refactor: move patch to v14 and update patches.txt (cherry picked from commit da2286802a1216bffe3a7d832340124eb091be4b) --- erpnext/patches.txt | 2 +- .../{v15_0 => v14_0}/update_pos_return_ledger_entries.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename erpnext/patches/{v15_0 => v14_0}/update_pos_return_ledger_entries.py (100%) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e426a47bb7d..797e8cebc80 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,7 +357,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request -erpnext.patches.v15_0.update_pos_return_ledger_entries +erpnext.patches.v14_0.update_pos_return_ledger_entries # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 diff --git a/erpnext/patches/v15_0/update_pos_return_ledger_entries.py b/erpnext/patches/v14_0/update_pos_return_ledger_entries.py similarity index 100% rename from erpnext/patches/v15_0/update_pos_return_ledger_entries.py rename to erpnext/patches/v14_0/update_pos_return_ledger_entries.py