From 1d162ffb8765413fbd14ac759131cef3e636289e Mon Sep 17 00:00:00 2001 From: Hossein Yousefian <86075967+ihosseinu@users.noreply.github.com> Date: Sun, 16 Apr 2023 11:03:16 +0330 Subject: [PATCH 01/44] get_incoming_rate_zero_in_rate_fix --- erpnext/stock/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index b8c5187b2c..d1d72e6eab 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -293,10 +293,10 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): in_rate = ( _get_fifo_lifo_rate(previous_stock_queue, args.get("qty") or 0, valuation_method) if previous_stock_queue - else 0 + else None ) elif valuation_method == "Moving Average": - in_rate = previous_sle.get("valuation_rate") or 0 + in_rate = previous_sle.get("valuation_rate") if in_rate is None: in_rate = get_valuation_rate( From 13d4f859238fcfda2d3db42cac518eae5eb8a1ff Mon Sep 17 00:00:00 2001 From: Hossein Yousefian <86075967+ihosseinu@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:50:09 +0330 Subject: [PATCH 02/44] get_incoming_rate_voucher_no_fix --- erpnext/stock/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index d1d72e6eab..feb0e2b6a5 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -269,8 +269,6 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): if isinstance(args, str): args = json.loads(args) - voucher_no = args.get("voucher_no") or args.get("name") - in_rate = None if (args.get("serial_no") or "").strip(): in_rate = get_avg_purchase_rate(args.get("serial_no")) @@ -299,6 +297,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True): in_rate = previous_sle.get("valuation_rate") if in_rate is None: + voucher_no = args.get("voucher_no") or args.get("name") in_rate = get_valuation_rate( args.get("item_code"), args.get("warehouse"), From a6cb6c6f4784194af96d15ece3499415be05d53e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 3 May 2023 09:51:58 +0530 Subject: [PATCH 03/44] fix: recalculate costs in SCR while reposting --- erpnext/stock/stock_ledger.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8b517bf1e0..aa272cbfa7 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -781,13 +781,19 @@ class update_entries_after(object): d.db_update() def update_rate_on_subcontracting_receipt(self, sle, outgoing_rate): - if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): - frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "rate", outgoing_rate) + if frappe.db.exists("Subcontracting Receipt Item", sle.voucher_detail_no): + frappe.db.set_value("Subcontracting Receipt Item", sle.voucher_detail_no, "rate", outgoing_rate) else: frappe.db.set_value( "Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate ) + scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True) + scr.set_missing_values() + scr.db_update() + for d in scr.items + scr.get("supplied_items", []): + d.db_update() + def get_serialized_values(self, sle): incoming_rate = flt(sle.incoming_rate) actual_qty = flt(sle.actual_qty) From e0b22edb2e4fba3224b3dfd26243bcf483a3d71c Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 4 May 2023 15:07:52 +0530 Subject: [PATCH 04/44] test: add test case --- .../test_subcontracting_receipt.py | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 72ed4d4e2e..dfb72c3356 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -6,7 +6,7 @@ import copy import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import cint, flt +from frappe.utils import add_days, cint, cstr, flt, today import erpnext from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -26,6 +26,9 @@ from erpnext.controllers.tests.test_subcontracting_controller import ( from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, +) from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import ( make_subcontracting_receipt, ) @@ -528,6 +531,69 @@ class TestSubcontractingReceipt(FrappeTestCase): # consumed_qty should be (accepted_qty * qty_consumed_per_unit) = (6 * 1) = 6 self.assertEqual(scr.supplied_items[0].consumed_qty, 6) + def test_supplied_items_cost_after_reposting(self): + # Set Backflush Based On as "BOM" + set_backflush_based_on("BOM") + + # Create Material Receipt for RM's + make_stock_entry( + item_code="_Test Item", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + posting_date=add_days(today(), -2), + ) + make_stock_entry( + item_code="_Test Item Home Desktop 100", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 10, + "rate": 100, + "fg_item": "_Test FG Item", + "fg_item_qty": 10, + }, + ] + + # Create Subcontracting Order + sco = get_subcontracting_order(service_items=service_items) + + # Transfer RM's + rm_items = get_rm_items(sco.supplied_items) + + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + # Create Subcontracting Receipt + scr = make_subcontracting_receipt(sco.name) + scr.save() + scr.submit() + + # Create Backdated Stock Reconciliation + sr = create_stock_reconciliation( + item_code=rm_items[0].get("item_code"), + warehouse="_Test Warehouse 1 - _TC", + qty=100, + rate=50, + posting_date=add_days(today(), -1), + ) + + # Cost should be updated in Subcontracting Receipt after reposting + prev_cost = scr.supplied_items[0].rate + scr.load_from_db() + self.assertNotEqual(scr.supplied_items[0].rate, prev_cost) + self.assertEqual(scr.supplied_items[0].rate, sr.items[0].valuation_rate) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From d6433f803b497511873acb7ff3889853e4b06e0e Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 12 May 2023 11:45:33 +0530 Subject: [PATCH 05/44] refactor(minor): rename function to be more descriptive --- erpnext/controllers/subcontracting_controller.py | 2 +- .../tests/test_subcontracting_controller.py | 2 +- .../subcontracting_order/subcontracting_order.py | 14 +++++++------- .../subcontracting_receipt.py | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 05754293b7..c3fa894e89 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -741,7 +741,7 @@ class SubcontractingController(StockController): sco_doc = frappe.get_doc("Subcontracting Order", sco) sco_doc.update_status() - def set_missing_values_in_additional_costs(self): + def calculate_additional_costs(self): self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs")) if self.total_additional_costs: diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 0e6fe95d45..4ea4fd11b4 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -36,7 +36,7 @@ class TestSubcontractingController(FrappeTestCase): sco.remove_empty_rows() self.assertEqual((len_before - 1), len(sco.service_items)) - def test_set_missing_values_in_additional_costs(self): + def test_calculate_additional_costs(self): sco = get_subcontracting_order(do_not_submit=1) rate_without_additional_cost = sco.items[0].rate diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index e6de72d494..39197332c1 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -77,22 +77,22 @@ class SubcontractingOrder(SubcontractingController): frappe.throw(_(msg)) def set_missing_values(self): - self.set_missing_values_in_additional_costs() - self.set_missing_values_in_service_items() - self.set_missing_values_in_supplied_items() - self.set_missing_values_in_items() + self.calculate_additional_costs() + self.calculate_service_costs() + self.calculate_supplied_items_qty_and_amount() + self.calculate_items_qty_and_amount() - def set_missing_values_in_service_items(self): + def calculate_service_costs(self): for idx, item in enumerate(self.get("service_items")): self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty - def set_missing_values_in_supplied_items(self): + def calculate_supplied_items_qty_and_amount(self): for item in self.get("items"): bom = frappe.get_doc("BOM", item.bom) rm_cost = sum(flt(rm_item.amount) for rm_item in bom.items) item.rm_cost_per_qty = rm_cost / flt(bom.quantity) - def set_missing_values_in_items(self): + def calculate_items_qty_and_amount(self): total_qty = total = 0 for item in self.items: item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4f8e045d70..2c84262273 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -113,9 +113,9 @@ class SubcontractingReceipt(SubcontractingController): @frappe.whitelist() def set_missing_values(self): - self.set_missing_values_in_additional_costs() - self.set_missing_values_in_supplied_items() - self.set_missing_values_in_items() + self.calculate_additional_costs() + self.calculate_supplied_items_qty_and_amount() + self.calculate_items_qty_and_amount() def set_available_qty_for_consumption(self): supplied_items_details = {} @@ -147,13 +147,13 @@ class SubcontractingReceipt(SubcontractingController): item.rm_item_code, 0 ) - def set_missing_values_in_supplied_items(self): + def calculate_supplied_items_qty_and_amount(self): for item in self.get("supplied_items") or []: item.amount = item.rate * item.consumed_qty self.set_available_qty_for_consumption() - def set_missing_values_in_items(self): + def calculate_items_qty_and_amount(self): rm_supp_cost = {} for item in self.get("supplied_items") or []: if item.reference_name in rm_supp_cost: From 9c72c2a6cb11197b292b0f589f312e038ac5cc3a Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 12 May 2023 11:46:32 +0530 Subject: [PATCH 06/44] refactor: use `calculate_items_qty_and_amount()` to update scr items rate --- erpnext/stock/stock_ledger.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index aa272cbfa7..c7f0acd453 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -785,13 +785,15 @@ class update_entries_after(object): frappe.db.set_value("Subcontracting Receipt Item", sle.voucher_detail_no, "rate", outgoing_rate) else: frappe.db.set_value( - "Subcontracting Receipt Supplied Item", sle.voucher_detail_no, "rate", outgoing_rate + "Subcontracting Receipt Supplied Item", + sle.voucher_detail_no, + {"rate": outgoing_rate, "amount": abs(sle.actual_qty) * outgoing_rate}, ) scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True) - scr.set_missing_values() + scr.calculate_items_qty_and_amount() scr.db_update() - for d in scr.items + scr.get("supplied_items", []): + for d in scr.items: d.db_update() def get_serialized_values(self, sle): From 38aaba5720b28d0360339b5fb87d955eab392f6f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 13 May 2023 13:00:05 +0530 Subject: [PATCH 07/44] fix: inventory dimension for inter company transfer return use case --- erpnext/controllers/accounts_controller.py | 3 + erpnext/controllers/stock_controller.py | 18 +- .../test_inventory_dimension.py | 171 ++++++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d0ec654162..3d930d67e0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -392,6 +392,9 @@ class AccountsController(TransactionBase): ) def validate_inter_company_reference(self): + if self.get("is_return"): + return + if self.doctype not in ("Purchase Invoice", "Purchase Receipt"): return diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 796a069651..09089be861 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -449,8 +449,22 @@ class StockController(AccountsController): "Delivery Note", "Stock Entry", ]: - if (sl_dict.actual_qty > 0 and self.doctype in ["Purchase Invoice", "Purchase Receipt"]) or ( - sl_dict.actual_qty < 0 and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"] + if ( + ( + sl_dict.actual_qty > 0 + and not self.get("is_return") + or sl_dict.actual_qty < 0 + and self.get("is_return") + ) + and self.doctype in ["Purchase Invoice", "Purchase Receipt"] + ) or ( + ( + sl_dict.actual_qty < 0 + and not self.get("is_return") + or sl_dict.actual_qty > 0 + and self.get("is_return") + ) + and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"] ): sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname) else: diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index ae5f521f2b..2d273c66fa 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -4,6 +4,7 @@ import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.tests.utils import FrappeTestCase +from frappe.utils import nowdate, nowtime from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( @@ -257,6 +258,8 @@ class TestInventoryDimension(FrappeTestCase): ) def test_for_purchase_sales_and_stock_transaction(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + create_inventory_dimension( reference_document="Store", type_of_transaction="Outward", @@ -319,6 +322,98 @@ class TestInventoryDimension(FrappeTestCase): self.assertEqual(entries[0].store, "Store 2") self.assertEqual(entries[0].actual_qty, -10.0) + return_dn = make_return_doc("Delivery Note", dn_doc.name) + return_dn.submit() + entries = get_voucher_sl_entries(return_dn.name, ["warehouse", "store", "actual_qty"]) + + self.assertEqual(entries[0].warehouse, warehouse) + self.assertEqual(entries[0].store, "Store 2") + self.assertEqual(entries[0].actual_qty, 10.0) + + se_doc = make_stock_entry( + item_code=item_code, qty=10, from_warehouse=warehouse, to_warehouse=warehouse, do_not_save=True + ) + + se_doc.items[0].store = "Store 2" + se_doc.items[0].to_store = "Store 1" + + se_doc.save() + se_doc.submit() + + return_pr = make_return_doc("Purchase Receipt", pr_doc.name) + return_pr.submit() + entries = get_voucher_sl_entries(return_pr.name, ["warehouse", "store", "actual_qty"]) + + self.assertEqual(entries[0].warehouse, warehouse) + self.assertEqual(entries[0].store, "Store 1") + self.assertEqual(entries[0].actual_qty, -10.0) + + def test_inter_transfer_return_against_inventory_dimension(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + + data = prepare_data_for_internal_transfer() + + dn_doc = create_delivery_note( + customer=data.customer, + company=data.company, + warehouse=data.from_warehouse, + target_warehouse=data.to_warehouse, + qty=5, + cost_center=data.cost_center, + expense_account=data.expense_account, + do_not_submit=True, + ) + + dn_doc.items[0].store = "Inter Transfer Store 1" + dn_doc.items[0].to_store = "Inter Transfer Store 2" + dn_doc.save() + dn_doc.submit() + + for d in get_voucher_sl_entries(dn_doc.name, ["store", "actual_qty"]): + if d.actual_qty > 0: + self.assertEqual(d.store, "Inter Transfer Store 2") + else: + self.assertEqual(d.store, "Inter Transfer Store 1") + + pr_doc = make_inter_company_purchase_receipt(dn_doc.name) + pr_doc.items[0].warehouse = data.store_warehouse + pr_doc.items[0].from_store = "Inter Transfer Store 2" + pr_doc.items[0].store = "Inter Transfer Store 3" + pr_doc.save() + pr_doc.submit() + + for d in get_voucher_sl_entries(pr_doc.name, ["store", "actual_qty"]): + if d.actual_qty > 0: + self.assertEqual(d.store, "Inter Transfer Store 3") + else: + self.assertEqual(d.store, "Inter Transfer Store 2") + + return_doc = make_return_doc("Purchase Receipt", pr_doc.name) + return_doc.submit() + + for d in get_voucher_sl_entries(return_doc.name, ["store", "actual_qty"]): + if d.actual_qty > 0: + self.assertEqual(d.store, "Inter Transfer Store 2") + else: + self.assertEqual(d.store, "Inter Transfer Store 3") + + dn_doc.load_from_db() + + return_doc1 = make_return_doc("Delivery Note", dn_doc.name) + return_doc1.posting_date = nowdate() + return_doc1.posting_time = nowtime() + return_doc1.items[0].target_warehouse = dn_doc.items[0].target_warehouse + return_doc1.items[0].warehouse = dn_doc.items[0].warehouse + return_doc1.save() + return_doc1.submit() + + for d in get_voucher_sl_entries(return_doc1.name, ["store", "actual_qty"]): + if d.actual_qty > 0: + self.assertEqual(d.store, "Inter Transfer Store 1") + else: + self.assertEqual(d.store, "Inter Transfer Store 2") + def get_voucher_sl_entries(voucher_no, fields): return frappe.get_all( @@ -423,3 +518,79 @@ def create_inventory_dimension(**args): doc.insert(ignore_permissions=True) return doc + + +def prepare_data_for_internal_transfer(): + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier + from erpnext.selling.doctype.customer.test_customer import create_internal_customer + from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + company = "_Test Company with perpetual inventory" + + customer = create_internal_customer( + "_Test Internal Customer 3", + company, + company, + ) + + supplier = create_internal_supplier( + "_Test Internal Supplier 3", + company, + company, + ) + + for store in ["Inter Transfer Store 1", "Inter Transfer Store 2", "Inter Transfer Store 3"]: + if not frappe.db.exists("Store", store): + frappe.get_doc({"doctype": "Store", "store_name": store}).insert(ignore_permissions=True) + + warehouse = create_warehouse("_Test Internal Warehouse New A", company=company) + + to_warehouse = create_warehouse("_Test Internal Warehouse GIT A", company=company) + + pr_doc = make_purchase_receipt( + company=company, warehouse=warehouse, qty=10, rate=100, do_not_submit=True + ) + pr_doc.items[0].store = "Inter Transfer Store 1" + pr_doc.submit() + + if not frappe.db.get_value("Company", company, "unrealized_profit_loss_account"): + account = "Unrealized Profit and Loss - TCP1" + if not frappe.db.exists("Account", account): + frappe.get_doc( + { + "doctype": "Account", + "account_name": "Unrealized Profit and Loss", + "parent_account": "Direct Income - TCP1", + "company": company, + "is_group": 0, + "account_type": "Income Account", + } + ).insert() + + frappe.db.set_value("Company", company, "unrealized_profit_loss_account", account) + + cost_center = frappe.db.get_value("Company", company, "cost_center") or frappe.db.get_value( + "Cost Center", {"company": company}, "name" + ) + + expene_account = frappe.db.get_value( + "Company", company, "stock_adjustment_account" + ) or frappe.db.get_value( + "Account", {"company": company, "account_type": "Expense Account"}, "name" + ) + + return frappe._dict( + { + "from_warehouse": warehouse, + "to_warehouse": to_warehouse, + "customer": customer, + "supplier": supplier, + "company": company, + "cost_center": cost_center, + "expene_account": expene_account, + "store_warehouse": frappe.db.get_value( + "Warehouse", {"name": ("like", "Store%"), "company": company}, "name" + ), + } + ) From 2b59a95162c89743794ea6a0bcf719ee85e4504c Mon Sep 17 00:00:00 2001 From: Phanupong Janthapoon Date: Sun, 14 May 2023 09:36:58 +0700 Subject: [PATCH 08/44] fix: share_transfer display wrong currency symbo In multi-companies setting, on amount and rate field of Share Transfer doctype are displaying wrong currency symbol, when create a Share Transfer of others company that has different currency than the main company. This due to the lack of `options` on those fields. To fix this, add `options` to `amount` and `rate` fields. --- erpnext/accounts/doctype/share_transfer/share_transfer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json index 59a305317d..51f2ac19bc 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.json +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json @@ -124,6 +124,7 @@ "fieldname": "rate", "fieldtype": "Currency", "label": "Rate", + "options": "Company:company:default_currency", "reqd": 1 }, { @@ -147,6 +148,7 @@ "fieldname": "amount", "fieldtype": "Currency", "label": "Amount", + "options": "Company:company:default_currency", "read_only": 1 }, { From e12e3bb0122fb280e17fd6ba7a436221adf00552 Mon Sep 17 00:00:00 2001 From: HarryPaulo Date: Sat, 13 May 2023 23:38:47 -0300 Subject: [PATCH 09/44] fix: allow search leads by doctype search fields * fix: allow search leads by doctype search fields * fix: allow search leads by doctype search fields, linters fix --- erpnext/controllers/queries.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 799fed99cc..f1cef71452 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -53,13 +53,17 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): doctype = "Lead" fields = get_fields(doctype, ["name", "lead_name", "company_name"]) + searchfields = frappe.get_meta(doctype).get_search_fields() + searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) + return frappe.db.sql( """select {fields} from `tabLead` where docstatus < 2 and ifnull(status, '') != 'Converted' and ({key} like %(txt)s or lead_name like %(txt)s - or company_name like %(txt)s) + or company_name like %(txt)s + or {scond}) {mcond} order by (case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end), @@ -68,7 +72,12 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, lead_name limit %(page_len)s offset %(start)s""".format( - **{"fields": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)} + **{ + "fields": ", ".join(fields), + "key": searchfield, + "scond": searchfields, + "mcond": get_match_cond(doctype), + } ), {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, ) From 26c5cfabdc71d706265c2a44432bb7a28986db49 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Sat, 13 May 2023 23:19:28 -0400 Subject: [PATCH 10/44] feat: copy project from timesheet to invoice (#35146) copy project from timesheet to invoice --- erpnext/projects/doctype/timesheet/timesheet.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index d482a46053..11156f4b50 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -374,6 +374,7 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None billing_rate = billing_amount / hours target.company = timesheet.company + target.project = timesheet.parent_project if customer: target.customer = customer From 19cd68778485525bcfcbc0105c4c2591e9aabff1 Mon Sep 17 00:00:00 2001 From: Daizy Modi Date: Sun, 14 May 2023 08:56:25 +0530 Subject: [PATCH 11/44] fix: function `batch_no` should only be declared once (#35115) fix: remove twice event call of `batch_no` to update batch qty --- erpnext/selling/sales_common.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index f1df3a11de..e3de49c57d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -299,7 +299,8 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } batch_no(doc, cdt, cdn) { - var me = this; + super.batch_no(doc, cdt, cdn); + var item = frappe.get_doc(cdt, cdn); if (item.serial_no) { @@ -378,10 +379,6 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } } - batch_no(doc, cdt, cdn) { - super.batch_no(doc, cdt, cdn); - } - qty(doc, cdt, cdn) { super.qty(doc, cdt, cdn); From 870b02b03cec923bedf1fa75ffdc488ca958ded7 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 14 May 2023 05:29:58 +0200 Subject: [PATCH 12/44] fix: allow over-payment against SO (#35079) --- erpnext/selling/doctype/sales_order/sales_order.js | 2 +- erpnext/startup/boot.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 449d461561..e9a6cc385d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -264,7 +264,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } // payment request - if(flt(doc.per_billed)<100) { + if(flt(doc.per_billed, precision('per_billed', doc)) < 100 + frappe.boot.sysdefaults.over_billing_allowance) { this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create')); this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create')); } diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 62936fcfb8..db1cc494e0 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -21,6 +21,10 @@ def boot_session(bootinfo): bootinfo.sysdefaults.allow_stale = cint( frappe.db.get_single_value("Accounts Settings", "allow_stale") ) + bootinfo.sysdefaults.over_billing_allowance = frappe.db.get_single_value( + "Accounts Settings", "over_billing_allowance" + ) + bootinfo.sysdefaults.quotation_valid_till = cint( frappe.db.get_single_value("CRM Settings", "default_valid_till") ) From 0c8276ec82c1d4b8cfd1d852b6e2e9a8c3cd3f1a Mon Sep 17 00:00:00 2001 From: "Indrajith.vs" <91895505+Gubbu77@users.noreply.github.com> Date: Sun, 14 May 2023 11:47:46 +0530 Subject: [PATCH 13/44] fix: sales person allocated amount calculation error nonetype and float (#35293) fix: sales person allocated amount calculation error nontype and float --- erpnext/controllers/selling_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index fc16a917d1..7687aad8b8 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -168,7 +168,7 @@ class SellingController(StockController): self.round_floats_in(sales_person) sales_person.allocated_amount = flt( - self.amount_eligible_for_commission * sales_person.allocated_percentage / 100.0, + flt(self.amount_eligible_for_commission) * sales_person.allocated_percentage / 100.0, self.precision("allocated_amount", sales_person), ) From f2ceb003797332cb6c1fee7a5c3e67a2b2b8e3d1 Mon Sep 17 00:00:00 2001 From: vishnu Date: Sun, 14 May 2023 09:35:38 +0000 Subject: [PATCH 14/44] fix: Creating landed cost voucher from connections --- erpnext/public/js/controllers/accounts.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index d943126018..47b88a002b 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -91,6 +91,12 @@ frappe.ui.form.on("Sales Invoice", { }); frappe.ui.form.on('Purchase Invoice', { + setup: (frm) => { + frm.make_methods = { + 'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') }, + } + }, + mode_of_payment: function(frm) { get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){ frm.set_value('cash_bank_account', account); @@ -99,6 +105,20 @@ frappe.ui.form.on('Purchase Invoice', { payment_terms_template: function() { cur_frm.trigger("disable_due_date"); + }, + + create_landedcost_voucher: function (frm) { + let lcv = frappe.model.get_new_doc('Landed Cost Voucher'); + lcv.company = frm.doc.company; + + let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice'); + lcv_receipt.receipt_document_type = 'Purchase Invoice'; + lcv_receipt.receipt_document = frm.doc.name; + lcv_receipt.supplier = frm.doc.supplier; + lcv_receipt.grand_total = frm.doc.grand_total; + lcv.purchase_receipts = [lcv_receipt]; + + frappe.set_route("Form", lcv.doctype, lcv.name); } }); From 2a609616d9da0eaf078ef3f6763aa4b643eafc97 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 15 May 2023 13:24:39 +0530 Subject: [PATCH 15/44] fix: port option for additional_conditions in item wise sales register (#35187) Co-authored-by: Deepesh Garg --- .../item_wise_sales_register.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index c987231fe1..dd9c073612 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -19,14 +19,19 @@ def execute(filters=None): return _execute(filters) -def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): +def _execute( + filters=None, + additional_table_columns=None, + additional_query_columns=None, + additional_conditions=None, +): if not filters: filters = {} columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") - item_list = get_items(filters, additional_query_columns) + item_list = get_items(filters, additional_query_columns, additional_conditions) if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) @@ -328,7 +333,7 @@ def get_columns(additional_table_columns, filters): return columns -def get_conditions(filters): +def get_conditions(filters, additional_conditions=None): conditions = "" for opts in ( @@ -341,6 +346,9 @@ def get_conditions(filters): if filters.get(opts[0]): conditions += opts[1] + if additional_conditions: + conditions += additional_conditions + if filters.get("mode_of_payment"): conditions += """ and exists(select name from `tabSales Invoice Payment` where parent=`tabSales Invoice`.name @@ -376,8 +384,8 @@ def get_group_by_conditions(filters, doctype): return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get("group_by"))) -def get_items(filters, additional_query_columns): - conditions = get_conditions(filters) +def get_items(filters, additional_query_columns, additional_conditions=None): + conditions = get_conditions(filters, additional_conditions) if additional_query_columns: additional_query_columns = ", " + ", ".join(additional_query_columns) From c236979508c00f4104fa2f45bcc925634f5905fd Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Mon, 15 May 2023 09:55:23 +0200 Subject: [PATCH 16/44] fix: Update de.csv (#35278) added many fixes on the base of 'No' which is often wrongly translated to 'Kein'. Also removed translation of Naming Seris like ACC-INV-.YYYY.- to ACC-INV-.YYYY.- --- erpnext/translations/de.csv | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 57556f3ef1..b4fd76d46c 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -7638,20 +7638,19 @@ Restaurant Order Entry Item,Restaurantbestellzugangsposten, Served,Serviert, Restaurant Reservation,Restaurant Reservierung, Waitlisted,Auf der Warteliste, -No Show,Keine Show, -No of People,Nein von Menschen, +No Show,Nicht angetreten, +No of People,Anzahl von Personen, Reservation Time,Reservierungszeit, Reservation End Time,Reservierungsendzeit, No of Seats,Anzahl der Sitze, Minimum Seating,Mindestbestuhlung, "Keep Track of Sales Campaigns. Keep track of Leads, Quotations, Sales Order etc from Campaigns to gauge Return on Investment. ","Verkaufskampagne verfolgen: Leads, Angebote, Aufträge usw. von Kampagnen beobachten um die Kapitalverzinsung (RoI) zu messen.", -SAL-CAM-.YYYY.-,SAL-CAM-.YYYY.-, Campaign Schedules,Kampagnenpläne, Buyer of Goods and Services.,Käufer von Waren und Dienstleistungen., -CUST-.YYYY.-,CUST-.YYYY.-, Default Company Bank Account,Standard-Bankkonto des Unternehmens, From Lead,Aus Lead, -Account Manager,Buchhalter, +Account Manager,Kundenberater, +Accounts Manager,Buchhalter, Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag, Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein, Default Price List,Standardpreisliste, @@ -7692,7 +7691,6 @@ Quantity of Items,Anzahl der Artikel, "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Fassen Sie eine Gruppe von Artikeln zu einem neuen Artikel zusammen. Dies ist nützlich, wenn Sie bestimmte Artikel zu einem Paket bündeln und einen Bestand an Artikel-Bündeln erhalten und nicht einen Bestand der einzelnen Artikel. Das Artikel-Bündel erhält für das Attribut ""Ist Lagerartikel"" den Wert ""Nein"" und für das Attribut ""Ist Verkaufsartikel"" den Wert ""Ja"". Beispiel: Wenn Sie Laptops und Tragetaschen getrennt verkaufen und einen bestimmten Preis anbieten, wenn der Kunde beides zusammen kauft, dann wird der Laptop mit der Tasche zusammen ein neuer Bündel-Artikel. Anmerkung: BOM = Stückliste", Parent Item,Übergeordneter Artikel, List items that form the package.,"Die Artikel auflisten, die das Paket bilden.", -SAL-QTN-.YYYY.-,SAL-QTN-.YYYY.-, Quotation To,Angebot für, Rate at which customer's currency is converted to company's base currency,"Kurs, zu dem die Währung des Kunden in die Basiswährung des Unternehmens umgerechnet wird", Rate at which Price list currency is converted to company's base currency,"Kurs, zu dem die Währung der Preisliste in die Basiswährung des Unternehmens umgerechnet wird", @@ -7704,7 +7702,6 @@ Quotation Item,Angebotsposition, Against Doctype,Zu DocType, Against Docname,Zu Dokumentenname, Additional Notes,Zusätzliche Bemerkungen, -SAL-ORD-.YYYY.-,SAL-ORD-.YYYY.-, Skip Delivery Note,Lieferschein überspringen, In Words will be visible once you save the Sales Order.,"""In Worten"" wird sichtbar, sobald Sie den Auftrag speichern.", Track this Sales Order against any Project,Diesen Auftrag in jedem Projekt nachverfolgen, @@ -7935,7 +7932,7 @@ For reference,Zu Referenzzwecken, Territory Targets,Ziele für die Region, Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,Artikelgruppenbezogene Budgets für diese Region erstellen. Durch Setzen der Auslieferungseinstellungen können auch saisonale Aspekte mit einbezogen werden., UOM Name,Maßeinheit-Name, -Check this to disallow fractions. (for Nos),"Hier aktivieren, um keine Bruchteile zuzulassen (für Nr.)", +Check this to disallow fractions. (for Nos),"Hier aktivieren, um keine Bruchteile zuzulassen (für Anzahl)", Website Item Group,Webseiten-Artikelgruppe, Cross Listing of Item in multiple groups,Kreuzweise Auflistung des Artikels in mehreren Gruppen, Default settings for Shopping Cart,Standardeinstellungen für den Warenkorb, @@ -8016,7 +8013,6 @@ Contact Information,Kontaktinformationen, Email sent to,E-Mail versandt an, Dispatch Information,Versandinformationen, Estimated Arrival,Voraussichtliche Ankunft, -MAT-DT-.YYYY.-,MAT-DT-.YYYY.-, Initial Email Notification Sent,Erste E-Mail-Benachrichtigung gesendet, Delivery Details,Lieferdetails, Driver Email,Fahrer-E-Mail, @@ -8176,7 +8172,6 @@ Purchase Receipt Item,Kaufbeleg-Artikel, Landed Cost Purchase Receipt,Einstandspreis-Kaufbeleg, Landed Cost Taxes and Charges,Einstandspreis Steuern und Gebühren, Landed Cost Voucher,Beleg über Einstandskosten, -MAT-LCV-.YYYY.-,MAT-LCV-.YYYY.-, Purchase Receipts,Kaufbelege, Purchase Receipt Items,Kaufbeleg-Artikel, Get Items From Purchase Receipts,Artikel vom Kaufbeleg übernehmen, @@ -8184,7 +8179,6 @@ Distribute Charges Based On,Kosten auf folgender Grundlage verteilen, Landed Cost Help,Hilfe zum Einstandpreis, Manufacturers used in Items,Hersteller im Artikel verwendet, Limited to 12 characters,Limitiert auf 12 Zeichen, -MAT-MR-.YYYY.-,MAT-MR-.YYYY.-, Partially Ordered,Teilweise bestellt, Transferred,Übergeben, % Ordered,% bestellt, @@ -8199,7 +8193,6 @@ Prevdoc DocType,Prevdoc DocType, Parent Detail docname,Übergeordnetes Detail Dokumentenname, "Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Packzettel für zu liefernde Pakete generieren. Wird verwendet, um Paketnummer, Packungsinhalt und das Gewicht zu dokumentieren.", Indicates that the package is a part of this delivery (Only Draft),"Zeigt an, dass das Paket ein Teil dieser Lieferung ist (nur Entwurf)", -MAT-PAC-.YYYY.-,MAT-PAC-.YYYY.-, From Package No.,Von Paket Nr., Identification of the package for the delivery (for print),Kennzeichnung des Paketes für die Lieferung (für den Druck), To Package No.,Bis Paket Nr., @@ -8290,7 +8283,6 @@ Under AMC,Innerhalb des jährlichen Wartungsvertrags, Out of AMC,Außerhalb des jährlichen Wartungsvertrags, Warranty Period (Days),Garantiefrist (Tage), Serial No Details,Details zur Seriennummer, -MAT-STE-.YYYY.-,MAT-STE-.JJJJ.-, Stock Entry Type,Bestandsbuchungsart, Stock Entry (Outward GIT),Bestandsbuchung (Outward GIT), Material Consumption for Manufacture,Materialverbrauch für die Herstellung, @@ -8336,7 +8328,6 @@ Stock Queue (FIFO),Lagerverfahren (FIFO), Is Cancelled,Ist storniert, Stock Reconciliation,Bestandsabgleich, This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.,"Dieses Werkzeug hilft Ihnen dabei, die Menge und die Bewertung von Bestand im System zu aktualisieren oder zu ändern. Es wird in der Regel verwendet, um die Systemwerte und den aktuellen Bestand Ihrer Lager zu synchronisieren.", -MAT-RECO-.YYYY.-,MAT-RECO-.YYYY.-, Reconciliation JSON,Abgleich JSON (JavaScript Object Notation), Stock Reconciliation Item,Bestandsabgleich-Artikel, Before reconciliation,Vor Ausgleich, @@ -8796,8 +8787,7 @@ Availed ITC State/UT Tax,Verfügbare ITC State / UT Tax, Availed ITC Cess,ITC Cess verfügbar, Is Nil Rated or Exempted,Ist gleich Null oder ausgenommen, Is Non GST,Ist nicht GST, -ACC-SINV-RET-.YYYY.-,ACC-SINV-RET-.YYYY.-, -E-Way Bill No.,E-Way Bill No., +E-Way Bill No.,E-Way Bill Nr., Is Consolidated,Ist konsolidiert, Billing Address GSTIN,Rechnungsadresse GSTIN, Customer GSTIN,Kunde GSTIN, @@ -9216,7 +9206,7 @@ Id,Ich würde, Time Required (In Mins),Erforderliche Zeit (in Minuten), From Posting Date,Ab dem Buchungsdatum, To Posting Date,Zum Buchungsdatum, -No records found,Keine Aufzeichnungen gefunden, +No records found,Keine Einträge gefunden, Customer/Lead Name,Name des Kunden / Lead, Unmarked Days,Nicht markierte Tage, Jan,Jan., @@ -9275,7 +9265,7 @@ Delay (in Days),Verzögerung (in Tagen), Group by Sales Order,Nach Auftrag gruppieren, Sales Value,Verkaufswert, Stock Qty vs Serial No Count,Lagermenge vs Seriennummer, -Serial No Count,Seriennummer nicht gezählt, +Serial No Count,Seriennummern gezählt, Work Order Summary,Arbeitsauftragsübersicht, Produce Qty,Menge produzieren, Lead Time (in mins),Vorlaufzeit (in Minuten), @@ -9569,7 +9559,7 @@ Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atl You can alternatively disable selling price validation in {} to bypass this validation.,"Alternativ können Sie die Validierung des Verkaufspreises in {} deaktivieren, um diese Validierung zu umgehen.", Invalid Selling Price,Ungültiger Verkaufspreis, Address needs to be linked to a Company. Please add a row for Company in the Links table.,Die Adresse muss mit einem Unternehmen verknüpft sein. Bitte fügen Sie eine Zeile für Firma in die Tabelle Links ein., -Company Not Linked,Firma nicht verbunden, +Company Not Linked,Firma nicht verknüpft, Import Chart of Accounts from CSV / Excel files,Kontenplan aus CSV / Excel-Dateien importieren, Completed Qty cannot be greater than 'Qty to Manufacture',Die abgeschlossene Menge darf nicht größer sein als die Menge bis zur Herstellung., "Row {0}: For Supplier {1}, Email Address is Required to send an email","Zeile {0}: Für Lieferant {1} ist eine E-Mail-Adresse erforderlich, um eine E-Mail zu senden", @@ -9656,7 +9646,7 @@ Hide Customer's Tax ID from Sales Transactions,Steuer-ID des Kunden vor Verkaufs Action If Quality Inspection Is Not Submitted,Maßnahme Wenn keine Qualitätsprüfung eingereicht wird, Auto Insert Price List Rate If Missing,"Preisliste automatisch einfügen, falls fehlt", Automatically Set Serial Nos Based on FIFO,Seriennummern basierend auf FIFO automatisch einstellen, -Set Qty in Transactions Based on Serial No Input,Stellen Sie die Menge in Transaktionen basierend auf Seriennummer ohne Eingabe ein, +Set Qty in Transactions Based on Serial No Input,Setze die Anzahl in der Transaktion basierend auf den Seriennummern, Raise Material Request When Stock Reaches Re-order Level,"Erhöhen Sie die Materialanforderung, wenn der Lagerbestand die Nachbestellmenge erreicht", Notify by Email on Creation of Automatic Material Request,Benachrichtigen Sie per E-Mail über die Erstellung einer automatischen Materialanforderung, Allow Material Transfer from Delivery Note to Sales Invoice,Materialübertragung vom Lieferschein zur Ausgangsrechnung zulassen, @@ -9765,7 +9755,7 @@ Open Form View,Öffnen Sie die Formularansicht, POS invoice {0} created succesfully,POS-Rechnung {0} erfolgreich erstellt, Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.,Lagermenge nicht ausreichend für Artikelcode: {0} unter Lager {1}. Verfügbare Menge {2}., Serial No: {0} has already been transacted into another POS Invoice.,Seriennummer: {0} wurde bereits in eine andere POS-Rechnung übertragen., -Balance Serial No,Balance Seriennr, +Balance Serial No,Stand Seriennummern, Warehouse: {0} does not belong to {1},Lager: {0} gehört nicht zu {1}, Please select batches for batched item {0},Bitte wählen Sie Chargen für Chargenartikel {0} aus, Please select quantity on row {0},Bitte wählen Sie die Menge in Zeile {0}, From 0489e302442a6e8a643dbabda922d8c2258dd53e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 16 May 2023 00:48:52 +0530 Subject: [PATCH 17/44] fix: item list view not working --- erpnext/stock/doctype/item/item_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item_list.js b/erpnext/stock/doctype/item/item_list.js index 534b341920..22d38e8893 100644 --- a/erpnext/stock/doctype/item/item_list.js +++ b/erpnext/stock/doctype/item/item_list.js @@ -1,5 +1,5 @@ frappe.listview_settings['Item'] = { - add_fields: ["item_name", "stock_uom", "item_group", "image", "variant_of", + add_fields: ["item_name", "stock_uom", "item_group", "image", "has_variants", "end_of_life", "disabled"], filters: [["disabled", "=", "0"]], From 9feda1b82977c69ef75d2f64fe4a3df46655abe9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 16 May 2023 13:11:47 +0530 Subject: [PATCH 18/44] perf: cache and simplify queries for holiday list (#35315) --- erpnext/setup/doctype/employee/employee.py | 4 +++- erpnext/setup/doctype/holiday_list/holiday_list.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index ece5a7d554..566392c327 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -257,7 +257,9 @@ def get_employee_email(employee_doc): def get_holiday_list_for_employee(employee, raise_exception=True): if employee: - holiday_list, company = frappe.db.get_value("Employee", employee, ["holiday_list", "company"]) + holiday_list, company = frappe.get_cached_value( + "Employee", employee, ["holiday_list", "company"] + ) else: holiday_list = "" company = frappe.db.get_single_value("Global Defaults", "default_company") diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index fad827ad8a..84d0d35287 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -115,6 +115,8 @@ def is_holiday(holiday_list, date=None): if date is None: date = today() if holiday_list: - return bool(frappe.get_all("Holiday List", dict(name=holiday_list, holiday_date=date))) + return bool( + frappe.db.exists("Holiday", {"parent": holiday_list, "holiday_date": date}, cache=True) + ) else: return False From 5574d9a72d8e4d372e01035ab44a1254538feb50 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 16 May 2023 13:38:55 +0530 Subject: [PATCH 19/44] fix(UX): misc "home" onboarding improvements (#35319) * fix(UX): cleanup "Home" onboarding - Remove company, letterhead, data import etc from home onboarding step * fix(UX): Show quick entry for item * chore: fix copy here and there --- .../setup/module_onboarding/home/home.json | 19 +++++-------------- .../company_set_up/company_set_up.json | 4 ++-- .../create_a_customer/create_a_customer.json | 6 +++--- .../create_a_quotation.json | 4 ++-- .../create_a_supplier/create_a_supplier.json | 6 +++--- .../create_an_item/create_an_item.json | 8 ++++---- .../data_import/data_import.json | 4 ++-- .../letterhead/letterhead.json | 4 ++-- .../navigation_help/navigation_help.json | 6 +++--- 9 files changed, 26 insertions(+), 35 deletions(-) diff --git a/erpnext/setup/module_onboarding/home/home.json b/erpnext/setup/module_onboarding/home/home.json index f02fc454c0..516f12229c 100644 --- a/erpnext/setup/module_onboarding/home/home.json +++ b/erpnext/setup/module_onboarding/home/home.json @@ -22,24 +22,18 @@ "creation": "2021-11-22 12:19:15.888642", "docstatus": 0, "doctype": "Module Onboarding", - "documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup", + "documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup", "idx": 0, "is_complete": 0, - "modified": "2022-06-07 14:31:00.575193", + "modified": "2023-05-16 13:13:24.043792", "modified_by": "Administrator", "module": "Setup", "name": "Home", "owner": "Administrator", "steps": [ - { - "step": "Company Set Up" - }, { "step": "Navigation Help" }, - { - "step": "Data import" - }, { "step": "Create an Item" }, @@ -51,12 +45,9 @@ }, { "step": "Create a Quotation" - }, - { - "step": "Letterhead" } ], - "subtitle": "Company, Item, Customer, Supplier, Navigation Help, Data Import, Letter Head, Quotation", - "success_message": "Masters are all set up!", - "title": "Let's Set Up Some Masters" + "subtitle": "Item, Customer, Supplier, Navigation Help and Quotation", + "success_message": "You're ready to start your journey with ERPNext", + "title": "Let's begin your journey with ERPNext" } \ No newline at end of file diff --git a/erpnext/setup/onboarding_step/company_set_up/company_set_up.json b/erpnext/setup/onboarding_step/company_set_up/company_set_up.json index 6f6583231f..fae2de0112 100644 --- a/erpnext/setup/onboarding_step/company_set_up/company_set_up.json +++ b/erpnext/setup/onboarding_step/company_set_up/company_set_up.json @@ -5,11 +5,11 @@ "description": "# Set Up a Company\n\nA company is a legal entity for which you will set up your books of account and create accounting transactions. In ERPNext, you can create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company.\n", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:22:18.317423", + "modified": "2023-05-15 09:18:42.895537", "modified_by": "Administrator", "name": "Company Set Up", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json index f74d745be9..e1a8f90866 100644 --- a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json @@ -5,17 +5,17 @@ "description": "# Create a Customer\n\nThe Customer master is at the heart of your sales transactions. Customers are linked in Quotations, Sales Orders, Invoices, and Payments. Customers can be either numbered or identified by name (you would typically do this based on the number of customers you have).\n\nThrough Customer\u2019s master, you can effectively track essentials like:\n - Customer\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:20:31.197564", + "modified": "2023-05-16 12:54:54.112364", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", "reference_document": "Customer", "show_form_tour": 0, "show_full_form": 0, - "title": "Manage Customers", + "title": "Create a Customer", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json index 8bdb621c0a..92b45b4ff8 100644 --- a/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json +++ b/erpnext/setup/onboarding_step/create_a_quotation/create_a_quotation.json @@ -5,11 +5,11 @@ "description": "# Create a Quotation\n\nLet\u2019s get started with business transactions by creating your first Quotation. You can create a Quotation for an existing customer or a prospect. It will be an approved document, with items you sell and the proposed price + taxes applied. After completing the instructions, you will get a Quotation in a ready to share print format.", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:21:31.675330", + "modified": "2023-05-15 09:18:42.984170", "modified_by": "Administrator", "name": "Create a Quotation", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json index 9574141eaa..ef493fe00d 100644 --- a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json @@ -5,17 +5,17 @@ "description": "# Create a Supplier\n\nAlso known as Vendor, is a master at the center of your purchase transactions. Suppliers are linked in Request for Quotation, Purchase Orders, Receipts, and Payments. Suppliers can be either numbered or identified by name.\n\nThrough Supplier\u2019s master, you can effectively track essentials like:\n - Supplier\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:21:23.518301", + "modified": "2023-05-16 12:55:08.610113", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", "reference_document": "Supplier", "show_form_tour": 0, "show_full_form": 0, - "title": "Manage Suppliers", + "title": "Create a Supplier", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json index cd29683346..15f36bec81 100644 --- a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json +++ b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json @@ -6,18 +6,18 @@ "docstatus": 0, "doctype": "Onboarding Step", "form_tour": "Item General", - "idx": 0, + "idx": 1, "intro_video_url": "", "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:19:56.297772", + "modified": "2023-05-16 12:56:40.355878", "modified_by": "Administrator", "name": "Create an Item", "owner": "Administrator", "reference_document": "Item", "show_form_tour": 1, - "show_full_form": 1, - "title": "Manage Items", + "show_full_form": 0, + "title": "Create an Item", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/setup/onboarding_step/data_import/data_import.json b/erpnext/setup/onboarding_step/data_import/data_import.json index 4999a368d3..e5dd7da29d 100644 --- a/erpnext/setup/onboarding_step/data_import/data_import.json +++ b/erpnext/setup/onboarding_step/data_import/data_import.json @@ -5,11 +5,11 @@ "description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2022-06-07 14:28:51.390813", + "modified": "2023-05-15 09:18:42.962231", "modified_by": "Administrator", "name": "Data import", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/letterhead/letterhead.json b/erpnext/setup/onboarding_step/letterhead/letterhead.json index 8e1bb8ce82..584fd481ba 100644 --- a/erpnext/setup/onboarding_step/letterhead/letterhead.json +++ b/erpnext/setup/onboarding_step/letterhead/letterhead.json @@ -5,11 +5,11 @@ "description": "# Create a Letter Head\n\nA Letter Head contains your organization's name, logo, address, etc which appears at the header and footer portion in documents. You can learn more about Setting up Letter Head in ERPNext here.\n", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2021-12-15 14:21:39.037742", + "modified": "2023-05-15 09:18:42.995184", "modified_by": "Administrator", "name": "Letterhead", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json index cf07968bc7..f57818b9f7 100644 --- a/erpnext/setup/onboarding_step/navigation_help/navigation_help.json +++ b/erpnext/setup/onboarding_step/navigation_help/navigation_help.json @@ -2,14 +2,14 @@ "action": "Watch Video", "action_label": "Learn about Navigation options", "creation": "2021-11-22 12:09:52.233872", - "description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or awesome bar\u2019s shortcut.\n", + "description": "# Navigation in ERPNext\n\nEase of navigating and browsing around the ERPNext is one of our core strengths. In the following video, you will learn how to reach a specific feature in ERPNext via module page or AwesomeBar.", "docstatus": 0, "doctype": "Onboarding Step", - "idx": 0, + "idx": 1, "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2022-06-07 14:28:00.901082", + "modified": "2023-05-16 12:53:25.939908", "modified_by": "Administrator", "name": "Navigation Help", "owner": "Administrator", From 776a83066db63e84cc6e7dac804c581343f81ccd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 May 2023 15:54:46 +0530 Subject: [PATCH 20/44] fix: cancelled vouchers in tax withheld vouchers list (#35309) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 5c9168bf9c..ab7884d520 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -303,7 +303,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. apply_tds(frm) { var me = this; - + me.frm.set_value("tax_withheld_vouchers", []); if (!me.frm.doc.apply_tds) { me.frm.set_value("tax_withholding_category", ''); me.frm.set_df_property("tax_withholding_category", "hidden", 1); From 6e661e7c0e8251f458d699254423cfb76dc3b5dd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 16 May 2023 16:23:52 +0530 Subject: [PATCH 21/44] fix: force to do reposting for cancelled document --- erpnext/controllers/stock_controller.py | 3 +++ .../test_repost_item_valuation.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 09089be861..befde71775 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -770,6 +770,9 @@ class StockController(AccountsController): } ) + if self.docstatus == 2: + force = True + if force or future_sle_exists(args) or repost_required_for_queue(self): item_based_reposting = cint( frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting") diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 96ac4352dc..9c4d997b31 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -376,3 +376,19 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): accounts_settings.acc_frozen_upto = "" accounts_settings.save() + + def test_create_repost_entry_for_cancelled_document(self): + pr = make_purchase_receipt( + company="_Test Company with perpetual inventory", + warehouse="Stores - TCP1", + get_multiple_items=True, + ) + + self.assertTrue(pr.docstatus == 1) + self.assertFalse(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name})) + + pr.load_from_db() + + pr.cancel() + self.assertTrue(pr.docstatus == 2) + self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": pr.name})) From ae4e56747c63f0335259cb0949ae1d429d2de2a0 Mon Sep 17 00:00:00 2001 From: Ashish Shah Date: Wed, 10 May 2023 10:51:20 +0530 Subject: [PATCH 22/44] refactor: use 'flt' for base_total_taxes_and_charges difference_amount calculation is broken, as calculation gives NaN. Fix is make frm.doc.base_total_taxes_and_charges as flt(frm.doc.base_total_taxes_and_charges) --- 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 07761c7705..2843824934 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -904,7 +904,7 @@ frappe.ui.form.on('Payment Entry', { function(d) { return flt(d.amount) })); frm.set_value("difference_amount", difference_amount - total_deductions + - frm.doc.base_total_taxes_and_charges); + flt(frm.doc.base_total_taxes_and_charges)); frm.events.hide_unhide_fields(frm); }, From 0da6c1688b3d37b21d25de81e202e3451fd8dac9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 May 2023 18:57:42 +0530 Subject: [PATCH 23/44] fix: unable to create partial invoice with auto fetch terms enabled (#35285) fix: fetch so/po terms if auto fetch is enabled --- erpnext/controllers/accounts_controller.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3d930d67e0..78bb05671d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1682,6 +1682,9 @@ class AccountsController(TransactionBase): d.base_payment_amount = flt( d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount") ) + else: + self.fetch_payment_terms_from_order(po_or_so, doctype) + self.ignore_default_payment_terms_template = 1 def get_order_details(self): if self.doctype == "Sales Invoice": From 84b7c1bba09f89a390a17898b4d8fa665adcbc8f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 May 2023 13:40:33 +0530 Subject: [PATCH 24/44] fix: tds incorrectly calculated for invoice that are below threshold Two purchase invoices for the same supplier, using different tax withholding categories have this issue. | Category | single | cumulative | |----------+--------+------------| | cat1 | 100 | 500 | | cat2 | 1000 | 5000 | 1. PINV1 of net total: 105/- uses cat1. TDS is calculated as it breached single threshold 2. PINV2 of net total: 200/- uses cat2. TDS incorrectly calculated as PINV1 already has TDS calculated and 'consider_party_ledger_amount' is enabled. --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index ad3477ef3d..1f2d980373 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -302,7 +302,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): "docstatus": 1, } - if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice": + if doctype != "Sales Invoice": filters.update( {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) From adf2474d9ded01ed89aedddd1fd3da3514cd68b0 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 16 May 2023 15:12:47 +0530 Subject: [PATCH 25/44] fix(ux): SCR consumed-qty read-only property --- .../subcontracting_receipt.js | 18 ++++-------------- .../subcontracting_receipt.py | 8 ++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js index 45289b1dab..4bf008ac40 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js @@ -76,26 +76,14 @@ frappe.ui.form.on('Subcontracting Receipt', { } }); - let batch_no_field = frm.get_docfield("items", "batch_no"); + let batch_no_field = frm.get_docfield('items', 'batch_no'); if (batch_no_field) { batch_no_field.get_route_options_for_new_doc = function(row) { return { - "item": row.doc.item_code + 'item': row.doc.item_code } }; } - - frappe.db.get_single_value('Buying Settings', 'backflush_raw_materials_of_subcontract_based_on').then(val => { - if (val == 'Material Transferred for Subcontract') { - frm.fields_dict['supplied_items'].grid.grid_rows.forEach((grid_row) => { - grid_row.docfields.forEach((df) => { - if (df.fieldname == 'consumed_qty') { - df.read_only = 0; - } - }); - }); - } - }); }, refresh: (frm) => { @@ -157,6 +145,8 @@ frappe.ui.form.on('Subcontracting Receipt', { } }); }, __('Get Items From')); + + frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', frm.doc.__onload && frm.doc.__onload.backflush_based_on === 'BOM'); } }, diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 2c84262273..416f4f80a2 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -28,6 +28,14 @@ class SubcontractingReceipt(SubcontractingController): }, ] + def onload(self): + self.set_onload( + "backflush_based_on", + frappe.db.get_single_value( + "Buying Settings", "backflush_raw_materials_of_subcontract_based_on" + ), + ) + def update_status_updater_args(self): if cint(self.is_return): self.status_updater.extend( From e5c86bc2e82202522bd803f66dc1bdc00f8e4432 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 May 2023 16:04:49 +0530 Subject: [PATCH 26/44] fix: consider 0 if rate/qty are null (#35338) [skip ci] --- .../patches/v13_0/update_amt_in_work_order_required_items.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py index e37f291233..64170edb71 100644 --- a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py +++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py @@ -7,4 +7,6 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") frappe.reload_doc("manufacturing", "doctype", "work_order_item") - frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""") + frappe.db.sql( + """UPDATE `tabWork Order Item` SET amount = ifnull(rate, 0.0) * ifnull(required_qty, 0.0)""" + ) From 9fb8b1827daa126087d3e237d135a96e73841e70 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Thu, 11 May 2023 12:47:47 +0530 Subject: [PATCH 27/44] fix: Pick List Status --- erpnext/stock/doctype/pick_list/pick_list.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 46d6e9e757..74927c779c 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -29,6 +29,7 @@ class PickList(Document): self.validate_for_qty() def before_save(self): + self.update_status() self.set_item_locations() # set percentage picked in SO @@ -89,20 +90,20 @@ class PickList(Document): self.update_reference_qty() self.update_sales_order_picking_status() - def update_status(self, status=None, update_modified=True): + def update_status(self, status=None): if not status: if self.docstatus == 0: status = "Draft" elif self.docstatus == 1: - if self.status == "Draft": - status = "Open" - elif target_document_exists(self.name, self.purpose): + if target_document_exists(self.name, self.purpose): status = "Completed" + else: + status = "Open" elif self.docstatus == 2: status = "Cancelled" if status: - frappe.db.set_value("Pick List", self.name, "status", status, update_modified=update_modified) + self.db_set("status", status) def update_reference_qty(self): packed_items = [] From 2b8c39eee9dd6b6e42449ac583797b82d00c1213 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 May 2023 22:05:46 +0530 Subject: [PATCH 28/44] fix: account closing balance patch --- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e158df63ff..7e68ec1fcb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -326,7 +326,7 @@ erpnext.patches.v13_0.update_docs_link erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances #10-05-2023 +erpnext.patches.v14_0.update_closing_balances #17-05-2023 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) # below migration patches should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index bb108ab827..d66467775c 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -13,8 +13,8 @@ from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") + i = 0 company_wise_order = {} - get_opening_entries = True for pcv in frappe.db.get_all( "Period Closing Voucher", fields=["company", "posting_date", "name"], @@ -29,6 +29,7 @@ def execute(): pcv.posting_date, pcv.fiscal_year, company=pcv.company )[1] + # get gl entries against pcv gl_entries = frappe.db.get_all( "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] ) @@ -37,20 +38,31 @@ def execute(): entry["closing_date"] = pcv_doc.posting_date entry["period_closing_voucher"] = pcv_doc.name + # get all gl entries for the year closing_entries = frappe.db.get_all( "GL Entry", filters={ "is_cancelled": 0, "voucher_no": ["!=", pcv.name], - "posting_date": ["<=", pcv.posting_date], + "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], + "is_opening": "No", }, fields=["*"], ) + if i == 0: + # add opening entries only for the first pcv + closing_entries += frappe.db.get_all( + "GL Entry", + filters={"is_cancelled": 0, "is_opening": "Yes"}, + fields=["*"], + ) + for entry in closing_entries: entry["closing_date"] = pcv_doc.posting_date entry["period_closing_voucher"] = pcv_doc.name make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) company_wise_order[pcv.company].append(pcv.posting_date) - get_opening_entries = False + + i += 1 From 8c53e0f0045cc8ecdcd61dff3b4dc651b2c34e0c Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 17 May 2023 22:29:09 +0530 Subject: [PATCH 29/44] fix: depreciation schedule for existing assets [dev] (#35256) * fix: depreciation schedule for existing assets * chore: correct logic for existing assets and fix test * chore: properly check if depr amount is non zero --- erpnext/assets/doctype/asset/test_asset.py | 2 +- .../asset_depreciation_schedule.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 203612ff1b..f3a9ba0948 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -691,7 +691,7 @@ class TestDepreciationMethods(AssetSetup): ) self.assertEqual(asset.status, "Draft") - expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]] + expected_schedules = [["2032-12-31", 42904.11, 90000.0]] schedules = [ [cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount] for d in get_depr_schedule(asset.name, "Draft") 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 116593ad9e..ad5ec3d6ed 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -337,7 +337,7 @@ class AssetDepreciationSchedule(Document): depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life skip_row = True - if depreciation_amount > 0: + if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: self.add_depr_schedule_row( schedule_date, depreciation_amount, @@ -521,9 +521,11 @@ def get_straight_line_or_manual_depr_amount(asset, row): ) # if the Depreciation Schedule is being prepared for the first time else: - return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt( - row.total_number_of_depreciations - ) + return ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) def get_wdv_or_dd_depr_amount( From 90ddf1c0f0bd7e7bd8a932d3e21c0829cf60ff28 Mon Sep 17 00:00:00 2001 From: MOHAMMED NIYAS <76736615+niyazrazak@users.noreply.github.com> Date: Thu, 18 May 2023 10:23:58 +0530 Subject: [PATCH 30/44] fix: create sales order Getting error while clicking create sales order button from quotation Taceback (most recent call last): File "apps/frappe/frappe/app.py", line 66, in application response = frappe.api.handle() File "apps/frappe/frappe/api.py", line 54, in handle return frappe.handler.handle() File "apps/frappe/frappe/handler.py", line 45, in handle data = execute_cmd(cmd) File "apps/frappe/frappe/handler.py", line 83, in execute_cmd return frappe.call(method, **frappe.form_dict) File "apps/frappe/frappe/__init__.py", line 1607, in call return fn(*args, **newargs) File "apps/frappe/frappe/model/mapper.py", line 36, in make_mapped_doc return method(source_name) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 263, in make_sales_order return _make_sales_order(source_name, target_doc) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 333, in _make_sales_order doclist = get_mapped_doc( File "apps/frappe/frappe/model/mapper.py", line 144, in get_mapped_doc postprocess(source_doc, target_doc) File "apps/erpnext/erpnext/selling/doctype/quotation/quotation.py", line 291, in set_missing_values for d in customer.get("sales_team"): TypeError: 'NoneType' object is not iterable --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 693fc92ce9..0cab21fa30 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): ) # sales team - for d in customer.get("sales_team"): + for d in customer.get("sales_team", []): target.append( "sales_team", { From 132846bbd107a921dfbfde9240f368bea9382cf7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 May 2023 12:00:59 +0530 Subject: [PATCH 31/44] fix(test): cumulative threshold checks --- .../test_tax_withholding_category.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 1e86cf5d2e..bc4f6709fc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -110,9 +110,9 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices.append(pi1) # Cumulative threshold is 30000 - # Threshold calculation should be on both the invoices - # TDS should be applied only on 1000 - self.assertEqual(pi1.taxes[0].tax_amount, 1000) + # Threshold calculation should be only on the Second invoice + # Second didn't breach, no TDS should be applied + self.assertEqual(pi1.taxes, []) for d in reversed(invoices): d.cancel() From b2290c6f57cfe8a9ddade44f673039a4600313c9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 May 2023 12:47:38 +0530 Subject: [PATCH 32/44] fix: possible type error on quotation -> sales order creation --- erpnext/selling/doctype/quotation/quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 0cab21fa30..61969fe8a9 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): ) # sales team - for d in customer.get("sales_team", []): + for d in customer.get("sales_team") or []: target.append( "sales_team", { From 7b818e9d34c87ad2fd80493454bd86674ddb54c5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 19 May 2023 18:53:40 +0530 Subject: [PATCH 33/44] feat: provision to make reposting entries from Stock and Account Value Comparison Report --- .../stock_and_account_value_comparison.js | 37 ++++++++++++++++++- .../stock_and_account_value_comparison.py | 33 +++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js index 7a170beec3..50a78a8258 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js @@ -33,5 +33,40 @@ frappe.query_reports["Stock and Account Value Comparison"] = { "fieldtype": "Date", "default": frappe.datetime.get_today(), }, - ] + ], + + get_datatable_options(options) { + return Object.assign(options, { + checkboxColumn: true, + }); + }, + + onload(report) { + report.page.add_inner_button(__("Create Reposting Entries"), function() { + let message = `
+

+ Reposting Entries will change the value of + accounts Stock In Hand, and Stock Expenses + in the Trial Balance report and will also change + the Balance Value in the Stock Balance report. +

+

Are you sure you want to create Reposting Entries?

+
+ `; + + frappe.confirm(__(message), () => { + let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); + let selected_rows = indexes.map(i => frappe.query_report.data[i]); + + frappe.call({ + method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries", + args: { + rows: selected_rows, + company: frappe.query_report.get_filter_values().company + } + }); + + }); + }); + } }; diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 106e877c4c..b1da3ec1bd 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.utils import get_link_to_form, parse_json import erpnext from erpnext.accounts.utils import get_currency_precision, get_stock_accounts @@ -134,3 +135,35 @@ def get_columns(filters): "width": "120", }, ] + + +@frappe.whitelist() +def create_reposting_entries(rows, company): + if isinstance(rows, str): + rows = parse_json(rows) + + entries = [] + for row in rows: + row = frappe._dict(row) + + try: + doc = frappe.get_doc( + { + "doctype": "Repost Item Valuation", + "based_on": "Transaction", + "status": "Queued", + "voucher_type": row.voucher_type, + "voucher_no": row.voucher_no, + "posting_date": row.posting_date, + "company": company, + "allow_nagative_stock": 1, + } + ).submit() + + entries.append(get_link_to_form("Repost Item Valuation", doc.name)) + except frappe.DuplicateEntryError: + pass + + if entries: + entries = ", ".join(entries) + frappe.msgprint(_(f"Reposting entries created: {entries}")) From ba61865ee647891f91d38b6a6cd4c2350a05962e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 20 May 2023 19:45:59 +0530 Subject: [PATCH 34/44] chore: drop outdated navigation video new stuff is in work, this one is actually counter-productive rn. --- erpnext/setup/module_onboarding/home/home.json | 7 ++----- .../create_a_customer/create_a_customer.json | 2 +- .../create_a_supplier/create_a_supplier.json | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/module_onboarding/home/home.json b/erpnext/setup/module_onboarding/home/home.json index 516f12229c..1fd96796cb 100644 --- a/erpnext/setup/module_onboarding/home/home.json +++ b/erpnext/setup/module_onboarding/home/home.json @@ -25,15 +25,12 @@ "documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup", "idx": 0, "is_complete": 0, - "modified": "2023-05-16 13:13:24.043792", + "modified": "2023-05-20 19:45:03.936741", "modified_by": "Administrator", "module": "Setup", "name": "Home", "owner": "Administrator", "steps": [ - { - "step": "Navigation Help" - }, { "step": "Create an Item" }, @@ -47,7 +44,7 @@ "step": "Create a Quotation" } ], - "subtitle": "Item, Customer, Supplier, Navigation Help and Quotation", + "subtitle": "Item, Customer, Supplier and Quotation", "success_message": "You're ready to start your journey with ERPNext", "title": "Let's begin your journey with ERPNext" } \ No newline at end of file diff --git a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json index e1a8f90866..5b0fd419c2 100644 --- a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json @@ -9,7 +9,7 @@ "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2023-05-16 12:54:54.112364", + "modified": "2023-05-16 20:01:34.202622", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json index ef493fe00d..4ac26e2879 100644 --- a/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/setup/onboarding_step/create_a_supplier/create_a_supplier.json @@ -9,7 +9,7 @@ "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2023-05-16 12:55:08.610113", + "modified": "2023-05-19 15:32:55.069257", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", From 57ee473fa40981ccb89acc91c608b6387a6b8278 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 21 May 2023 15:07:38 +0530 Subject: [PATCH 35/44] fix: don't recalculate rate for SCR rejected warehouse SLE --- erpnext/controllers/subcontracting_controller.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index c3fa894e89..1e9c4dc847 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -689,7 +689,6 @@ class SubcontractingController(StockController): "actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor), "serial_no": cstr(item.rejected_serial_no).strip(), "incoming_rate": 0.0, - "recalculate_rate": 1, }, ) ) From a209fb4b64cfc43f6bf266a6f96275226d2c2707 Mon Sep 17 00:00:00 2001 From: vishnu Date: Mon, 22 May 2023 18:00:50 +0000 Subject: [PATCH 36/44] fix: error while saving job card --- erpnext/manufacturing/doctype/job_card/job_card.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 85061113ce..bb84b6f201 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -120,7 +120,8 @@ "fieldname": "for_quantity", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty To Manufacture" + "label": "Qty To Manufacture", + "reqd": 1 }, { "fieldname": "wip_warehouse", @@ -439,7 +440,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2022-11-09 15:02:44.490731", + "modified": "2023-05-22 23:26:57.589331", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", From 64751ec4d9bfd7a7f6d7e5b9b959035dc4dc3ba9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 May 2023 22:23:37 +0530 Subject: [PATCH 37/44] feat: provision to skip available sub assembly items in the production plan --- .../production_plan/production_plan.json | 32 ++++- .../production_plan/production_plan.py | 111 +++++++++++++++++- .../production_plan_sub_assembly_item.json | 30 ++++- 3 files changed, 161 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index fdaa4a2a1d..232f1cb2c4 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -35,8 +35,12 @@ "section_break_25", "prod_plan_references", "section_break_24", - "get_sub_assembly_items", "combine_sub_items", + "section_break_ucc4", + "skip_available_sub_assembly_item", + "column_break_igxl", + "get_sub_assembly_items", + "section_break_g4ip", "sub_assembly_items", "download_materials_request_plan_section_section", "download_materials_required", @@ -351,12 +355,12 @@ { "fieldname": "section_break_24", "fieldtype": "Section Break", - "hide_border": 1 + "hide_border": 1, + "label": "Sub Assembly Items" }, { "fieldname": "sub_assembly_items", "fieldtype": "Table", - "label": "Sub Assembly Items", "no_copy": 1, "options": "Production Plan Sub Assembly Item" }, @@ -392,13 +396,33 @@ "fieldname": "download_materials_request_plan_section_section", "fieldtype": "Section Break", "label": "Download Materials Request Plan Section" + }, + { + "default": "0", + "description": "System consider the projected quantity to check available or will be available sub-assembly items ", + "fieldname": "skip_available_sub_assembly_item", + "fieldtype": "Check", + "label": "Skip Available Sub Assembly Items" + }, + { + "fieldname": "section_break_ucc4", + "fieldtype": "Column Break", + "hide_border": 1 + }, + { + "fieldname": "section_break_g4ip", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_igxl", + "fieldtype": "Column Break" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-03-31 10:30:48.118932", + "modified": "2023-05-22 23:36:31.770517", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index f9e68b916f..e40539acf3 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -718,7 +718,9 @@ class ProductionPlan(Document): frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx)) bom_data = [] - get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty) + + warehouse = row.warehouse if self.skip_available_sub_assembly_item else None + get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) sub_assembly_items_store.extend(bom_data) @@ -894,7 +896,9 @@ def download_raw_materials(doc, warehouses=None): build_csv_response(item_list, doc.name) -def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1): +def get_exploded_items( + item_details, company, bom_no, include_non_stock_items, planned_qty=1, doc=None +): bei = frappe.qb.DocType("BOM Explosion Item") bom = frappe.qb.DocType("BOM") item = frappe.qb.DocType("Item") @@ -1271,6 +1275,12 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d include_safety_stock = doc.get("include_safety_stock") so_item_details = frappe._dict() + + sub_assembly_items = {} + if doc.get("skip_available_sub_assembly_item"): + for d in doc.get("sub_assembly_items"): + sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty")) + for data in po_items: if not data.get("include_exploded_items") and doc.get("sub_assembly_items"): data["include_exploded_items"] = 1 @@ -1296,10 +1306,24 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx"))) if bom_no: - if data.get("include_exploded_items") and include_subcontracted_items: + if ( + data.get("include_exploded_items") + and doc.get("sub_assembly_items") + and doc.get("skip_available_sub_assembly_item") + ): + item_details = get_raw_materials_of_sub_assembly_items( + item_details, + company, + bom_no, + include_non_stock_items, + sub_assembly_items, + planned_qty=planned_qty, + ) + + elif data.get("include_exploded_items") and include_subcontracted_items: # fetch exploded items from BOM item_details = get_exploded_items( - item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty + item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty, doc=doc ) else: item_details = get_subitems( @@ -1456,12 +1480,22 @@ def get_item_data(item_code): } -def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0): +def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, company, warehouse=None, indent=0): data = get_bom_children(parent=bom_no) for d in data: if d.expandable: parent_item_code = frappe.get_cached_value("BOM", bom_no, "item") stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty) + + if warehouse: + bin_dict = get_bin_details(d, company, for_warehouse=warehouse) + + if bin_dict and bin_dict[0].projected_qty > 0: + if bin_dict[0].projected_qty > stock_qty: + continue + else: + stock_qty = stock_qty - bin_dict[0].projected_qty + bom_data.append( frappe._dict( { @@ -1481,7 +1515,7 @@ def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0): ) if d.value: - get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1) + get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1) def set_default_warehouses(row, default_warehouses): @@ -1519,3 +1553,68 @@ def get_reserved_qty_for_production_plan(item_code, warehouse): ) return reserved_qty_for_production_plan - reserved_qty_for_production + + +def get_raw_materials_of_sub_assembly_items( + item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1 +): + + bei = frappe.qb.DocType("BOM Item") + bom = frappe.qb.DocType("BOM") + item = frappe.qb.DocType("Item") + item_default = frappe.qb.DocType("Item Default") + item_uom = frappe.qb.DocType("UOM Conversion Detail") + + items = ( + frappe.qb.from_(bei) + .join(bom) + .on(bom.name == bei.parent) + .join(item) + .on(item.name == bei.item_code) + .left_join(item_default) + .on((item_default.parent == item.name) & (item_default.company == company)) + .left_join(item_uom) + .on((item.name == item_uom.parent) & (item_uom.uom == item.purchase_uom)) + .select( + (IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"), + item.item_name, + item.name.as_("item_code"), + bei.description, + bei.stock_uom, + bei.bom_no, + item.min_order_qty, + bei.source_warehouse, + item.default_material_request_type, + item.min_order_qty, + item_default.default_warehouse, + item.purchase_uom, + item_uom.conversion_factor, + item.safety_stock, + ) + .where( + (bei.docstatus == 1) + & (bom.name == bom_no) + & (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1) + ) + .groupby(bei.item_code, bei.stock_uom) + ).run(as_dict=True) + + for item in items: + key = (item.item_code, item.bom_no) + if item.bom_no and key in sub_assembly_items: + planned_qty = flt(sub_assembly_items[key]) + get_raw_materials_of_sub_assembly_items( + item_details, + company, + item.bom_no, + include_non_stock_items, + sub_assembly_items, + planned_qty=planned_qty, + ) + else: + if not item.conversion_factor and item.purchase_uom: + item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom) + + item_details.setdefault(item.get("item_code"), item) + + return item_details diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json index 4eb6bf6ecf..fde0404c01 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -28,7 +28,11 @@ "uom", "stock_uom", "column_break_22", - "description" + "description", + "section_break_4rxf", + "actual_qty", + "column_break_xfhm", + "projected_qty" ], "fields": [ { @@ -183,12 +187,34 @@ "fieldtype": "Datetime", "in_list_view": 1, "label": "Schedule Date" + }, + { + "fieldname": "section_break_4rxf", + "fieldtype": "Section Break" + }, + { + "fieldname": "actual_qty", + "fieldtype": "Float", + "label": "Actual Qty", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_xfhm", + "fieldtype": "Column Break" + }, + { + "fieldname": "projected_qty", + "fieldtype": "Float", + "label": "Projected Qty", + "no_copy": 1, + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-11-28 13:50:15.116082", + "modified": "2023-05-22 17:52:34.708879", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sub Assembly Item", From 9dd566c1e80320165475649a5c34542902e00e4e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 23 May 2023 01:02:16 +0530 Subject: [PATCH 38/44] test: test case to skip available qty for sub-assembly items --- .../production_plan/test_production_plan.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 4648d896ce..75b43ec1c3 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -926,6 +926,50 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(after_qty, before_qty) + def test_skip_available_qty_for_sub_assembly_items(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + bom_tree = { + "Fininshed Goods1 For SUB Test": { + "SubAssembly1 For SUB Test": {"ChildPart1 For SUB Test": {}}, + "SubAssembly2 For SUB Test": {}, + } + } + + parent_bom = create_nested_bom(bom_tree, prefix="") + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=10, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse="_Test Warehouse - _TC", + ) + + make_stock_entry( + item_code="SubAssembly1 For SUB Test", + qty=5, + rate=100, + target="_Test Warehouse - _TC", + ) + + self.assertTrue(plan.skip_available_sub_assembly_item) + + plan.get_sub_assembly_items() + + for row in plan.sub_assembly_items: + if row.production_item == "SubAssembly1 For SUB Test": + self.assertEqual(row.qty, 5) + + mr_items = get_items_for_material_requests(plan.as_dict()) + for row in mr_items: + row = frappe._dict(row) + if row.item_code == "ChildPart1 For SUB Test": + self.assertEqual(row.quantity, 5) + + if row.item_code == "SubAssembly2 For SUB Test": + self.assertEqual(row.quantity, 10) + def create_production_plan(**args): """ @@ -945,6 +989,7 @@ def create_production_plan(**args): "include_subcontracted_items": args.include_subcontracted_items or 0, "ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0, "get_items_from": "Sales Order", + "skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0, } ) @@ -958,6 +1003,7 @@ def create_production_plan(**args): "planned_qty": args.planned_qty or 1, "planned_start_date": args.planned_start_date or now_datetime(), "stock_uom": args.stock_uom or "Nos", + "warehouse": args.warehouse, }, ) From 8c34cc0e00980d1968e4daee07a88fd85a0b4a52 Mon Sep 17 00:00:00 2001 From: vishnu Date: Tue, 23 May 2023 04:27:50 +0000 Subject: [PATCH 39/44] fix: use flt instead of mandatory field --- erpnext/manufacturing/doctype/job_card/job_card.json | 5 ++--- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index bb84b6f201..316e586b7a 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -120,8 +120,7 @@ "fieldname": "for_quantity", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty To Manufacture", - "reqd": 1 + "label": "Qty To Manufacture" }, { "fieldname": "wip_warehouse", @@ -440,7 +439,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2023-05-22 23:26:57.589331", + "modified": "2023-05-23 09:56:43.826602", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 4a4046e47a..a7d0b29f83 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -730,7 +730,7 @@ class JobCard(Document): self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0] if self.docstatus < 2: - if self.for_quantity <= self.transferred_qty: + if flt(self.for_quantity) <= flt(self.transferred_qty): self.status = "Material Transferred" if self.time_logs: From a1119171141d64eac0a6bb1227c101c93645d8be Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 19 May 2023 12:57:47 +0530 Subject: [PATCH 40/44] fix: Pick List TypeError --- erpnext/stock/doctype/pick_list/pick_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 74927c779c..a9a9a1d664 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -460,7 +460,7 @@ def get_items_with_location_and_quantity(item_doc, item_location_map, docstatus) item_doc.qty if (docstatus == 1 and item_doc.stock_qty == 0) else item_doc.stock_qty ) - while remaining_stock_qty > 0 and available_locations: + while flt(remaining_stock_qty) > 0 and available_locations: item_location = available_locations.pop(0) item_location = frappe._dict(item_location) From b0eb72ffac0f35cd04c79098c1087648aefb52ba Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 23 May 2023 15:35:55 +0530 Subject: [PATCH 41/44] fix: replace quotation with invoice in first onboarding (#35389) [skip ci] --- .../sales_invoice/sales_invoice.json | 41 +++++++++++++++++++ .../form_tour/quotation/quotation.json | 12 ++---- .../setup/module_onboarding/home/home.json | 7 +--- .../create_a_customer/create_a_customer.json | 2 +- .../create_an_item/create_an_item.json | 2 +- .../create_your_first_sales_invoice.json | 20 +++++++++ 6 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 erpnext/accounts/form_tour/sales_invoice/sales_invoice.json create mode 100644 erpnext/setup/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json diff --git a/erpnext/accounts/form_tour/sales_invoice/sales_invoice.json b/erpnext/accounts/form_tour/sales_invoice/sales_invoice.json new file mode 100644 index 0000000000..414b897d54 --- /dev/null +++ b/erpnext/accounts/form_tour/sales_invoice/sales_invoice.json @@ -0,0 +1,41 @@ +{ + "creation": "2023-05-23 09:58:17.235916", + "docstatus": 0, + "doctype": "Form Tour", + "first_document": 0, + "idx": 0, + "include_name_field": 0, + "is_standard": 1, + "modified": "2023-05-23 13:10:56.227127", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice", + "owner": "Administrator", + "reference_doctype": "Sales Invoice", + "save_on_complete": 1, + "steps": [ + { + "description": "Select a customer for whom this invoice is being prepared.", + "fieldname": "customer", + "fieldtype": "Link", + "has_next_condition": 1, + "is_table_field": 0, + "label": "Customer", + "next_step_condition": "eval: doc.customer", + "position": "Right", + "title": "Select Customer" + }, + { + "child_doctype": "Sales Invoice Item", + "description": "Select item that you have sold along with quantity and rate.", + "fieldname": "items", + "fieldtype": "Table", + "has_next_condition": 0, + "is_table_field": 0, + "parent_fieldname": "items", + "position": "Top", + "title": "Select Item" + } + ], + "title": "Sales Invoice" +} \ No newline at end of file diff --git a/erpnext/selling/form_tour/quotation/quotation.json b/erpnext/selling/form_tour/quotation/quotation.json index 2a2aa5e63e..8c977009fb 100644 --- a/erpnext/selling/form_tour/quotation/quotation.json +++ b/erpnext/selling/form_tour/quotation/quotation.json @@ -2,9 +2,11 @@ "creation": "2021-11-23 12:00:36.138824", "docstatus": 0, "doctype": "Form Tour", + "first_document": 0, "idx": 0, + "include_name_field": 0, "is_standard": 1, - "modified": "2021-11-23 12:02:48.010298", + "modified": "2023-05-23 12:51:48.684517", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -14,51 +16,43 @@ "steps": [ { "description": "Select a customer or lead for whom this quotation is being prepared. Let's select a Customer.", - "field": "", "fieldname": "quotation_to", "fieldtype": "Link", "has_next_condition": 0, "is_table_field": 0, "label": "Quotation To", - "parent_field": "", "position": "Right", "title": "Quotation To" }, { "description": "Select a specific Customer to whom this quotation will be sent.", - "field": "", "fieldname": "party_name", "fieldtype": "Dynamic Link", "has_next_condition": 0, "is_table_field": 0, "label": "Party", - "parent_field": "", "position": "Right", "title": "Party" }, { "child_doctype": "Quotation Item", "description": "Select an item for which you will be quoting a price.", - "field": "", "fieldname": "items", "fieldtype": "Table", "has_next_condition": 0, "is_table_field": 0, "label": "Items", - "parent_field": "", "parent_fieldname": "items", "position": "Bottom", "title": "Items" }, { "description": "You can select pre-populated Sales Taxes and Charges from here.", - "field": "", "fieldname": "taxes", "fieldtype": "Table", "has_next_condition": 0, "is_table_field": 0, "label": "Sales Taxes and Charges", - "parent_field": "", "position": "Bottom", "title": "Sales Taxes and Charges" } diff --git a/erpnext/setup/module_onboarding/home/home.json b/erpnext/setup/module_onboarding/home/home.json index 1fd96796cb..7b0d77ff50 100644 --- a/erpnext/setup/module_onboarding/home/home.json +++ b/erpnext/setup/module_onboarding/home/home.json @@ -25,7 +25,7 @@ "documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup", "idx": 0, "is_complete": 0, - "modified": "2023-05-20 19:45:03.936741", + "modified": "2023-05-23 13:20:19.703506", "modified_by": "Administrator", "module": "Setup", "name": "Home", @@ -38,10 +38,7 @@ "step": "Create a Customer" }, { - "step": "Create a Supplier" - }, - { - "step": "Create a Quotation" + "step": "Create Your First Sales Invoice" } ], "subtitle": "Item, Customer, Supplier and Quotation", diff --git a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json index 5b0fd419c2..dc07578122 100644 --- a/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/setup/onboarding_step/create_a_customer/create_a_customer.json @@ -9,7 +9,7 @@ "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2023-05-16 20:01:34.202622", + "modified": "2023-05-23 12:45:55.138580", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json index 15f36bec81..4115196ffa 100644 --- a/erpnext/setup/onboarding_step/create_an_item/create_an_item.json +++ b/erpnext/setup/onboarding_step/create_an_item/create_an_item.json @@ -11,7 +11,7 @@ "is_complete": 0, "is_single": 0, "is_skipped": 0, - "modified": "2023-05-16 12:56:40.355878", + "modified": "2023-05-23 12:43:08.484206", "modified_by": "Administrator", "name": "Create an Item", "owner": "Administrator", diff --git a/erpnext/setup/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/setup/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json new file mode 100644 index 0000000000..96fa68e8c9 --- /dev/null +++ b/erpnext/setup/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -0,0 +1,20 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:48:21.019019", + "description": "# All about sales invoice\n\nA Sales Invoice is a bill that you send to your Customers against which the Customer makes the payment. Sales Invoice is an accounting transaction. On submission of Sales Invoice, the system updates the receivable and books income against a Customer Account.", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2023-05-22 21:20:15.589644", + "modified_by": "Administrator", + "name": "Create Your First Sales Invoice", + "owner": "Administrator", + "reference_document": "Sales Invoice", + "show_form_tour": 1, + "show_full_form": 1, + "title": "Create Your First Sales Invoice ", + "validate_action": 1 +} \ No newline at end of file From edb7b0db8f65a2bcb38196c9dd5c259fb020d15a Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 24 May 2023 12:29:46 +0530 Subject: [PATCH 42/44] fix: incorrect depr schedule and posting dates on selling of existing assets [dev] (#35398) * fix: calc depr amount properly on selling of existing assets and fix incorrect posting dates * chore: get number_of_depreciations_booked properly * chore: fix test_gle_made_by_asset_sale_for_existing_asset --- .../doctype/sales_invoice/sales_invoice.py | 14 +++- erpnext/assets/doctype/asset/depreciation.py | 31 +++++--- erpnext/assets/doctype/asset/test_asset.py | 77 +++++++++++++++++++ .../asset_capitalization.py | 1 + .../asset_depreciation_schedule.py | 5 +- 5 files changed, 114 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e16b1b1195..7454332cd3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1180,7 +1180,12 @@ class SalesInvoice(SellingController): if self.is_return: fixed_asset_gl_entries = get_gl_entries_on_asset_regain( - asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") + asset, + item.base_net_amount, + item.finance_book, + self.get("doctype"), + self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", None) @@ -1208,7 +1213,12 @@ class SalesInvoice(SellingController): asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal( - asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") + asset, + item.base_net_amount, + item.finance_book, + self.get("doctype"), + self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", self.posting_date) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index f23ae2f165..c64b917e18 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -307,7 +307,7 @@ def scrap_asset(asset_name): je.company = asset.company je.remark = "Scrap Entry for asset {0}".format(asset_name) - for entry in get_gl_entries_on_asset_disposal(asset): + for entry in get_gl_entries_on_asset_disposal(asset, date): entry.update({"reference_type": "Asset", "reference_name": asset_name}) je.append("accounts", entry) @@ -434,8 +434,11 @@ def disposal_happens_in_the_future(posting_date_of_disposal): def get_gl_entries_on_asset_regain( - asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None + asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None ): + if not date: + date = getdate() + ( fixed_asset_account, asset, @@ -453,7 +456,7 @@ def get_gl_entries_on_asset_regain( "debit_in_account_currency": asset.gross_purchase_amount, "debit": asset.gross_purchase_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -463,7 +466,7 @@ def get_gl_entries_on_asset_regain( "credit_in_account_currency": accumulated_depr_amount, "credit": accumulated_depr_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -472,7 +475,7 @@ def get_gl_entries_on_asset_regain( profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount)) if profit_amount: get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date ) if voucher_type and voucher_no: @@ -484,8 +487,11 @@ def get_gl_entries_on_asset_regain( def get_gl_entries_on_asset_disposal( - asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None + asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None ): + if not date: + date = getdate() + ( fixed_asset_account, asset, @@ -503,7 +509,7 @@ def get_gl_entries_on_asset_disposal( "credit_in_account_currency": asset.gross_purchase_amount, "credit": asset.gross_purchase_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -513,7 +519,7 @@ def get_gl_entries_on_asset_disposal( "debit_in_account_currency": accumulated_depr_amount, "debit": accumulated_depr_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -522,7 +528,7 @@ def get_gl_entries_on_asset_disposal( profit_amount = flt(selling_amount) - flt(value_after_depreciation) if profit_amount: get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date ) if voucher_type and voucher_no: @@ -556,8 +562,11 @@ def get_asset_details(asset, finance_book=None): def get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date=None ): + if not date: + date = getdate() + debit_or_credit = "debit" if profit_amount < 0 else "credit" gl_entries.append( asset.get_gl_dict( @@ -566,7 +575,7 @@ def get_profit_gl_entries( "cost_center": depreciation_cost_center, debit_or_credit: abs(profit_amount), debit_or_credit + "_in_account_currency": abs(profit_amount), - "posting_date": getdate(), + "posting_date": date, }, item=asset, ) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index f3a9ba0948..c64f29699d 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -356,6 +356,83 @@ class TestAsset(AssetSetup): si.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") + def test_gle_made_by_asset_sale_for_existing_asset(self): + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + asset = create_asset( + calculate_depreciation=1, + available_for_use_date="2020-04-01", + purchase_date="2020-04-01", + expected_value_after_useful_life=0, + total_number_of_depreciations=5, + number_of_depreciations_booked=2, + frequency_of_depreciation=12, + depreciation_start_date="2023-03-31", + opening_accumulated_depreciation=24000, + gross_purchase_amount=60000, + submit=1, + ) + + expected_depr_values = [ + ["2023-03-31", 12000, 36000], + ["2024-03-31", 12000, 48000], + ["2025-03-31", 12000, 60000], + ] + + first_asset_depr_schedule = get_depr_schedule(asset.name, "Active") + + for i, schedule in enumerate(first_asset_depr_schedule): + self.assertEqual(getdate(expected_depr_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_depr_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_depr_values[i][2], schedule.accumulated_depreciation_amount) + + post_depreciation_entries(date="2023-03-31") + + si = create_sales_invoice( + item_code="Macbook Pro", asset=asset.name, qty=1, rate=40000, posting_date=getdate("2023-05-23") + ) + asset.load_from_db() + + self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") + + expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1742.47, 37742.47]] + + second_asset_depr_schedule = get_depr_schedule(asset.name, "Active") + + for i, schedule in enumerate(second_asset_depr_schedule): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + self.assertTrue(schedule.journal_entry) + + expected_gle = ( + ( + "_Test Accumulated Depreciations - _TC", + 37742.47, + 0.0, + ), + ( + "_Test Fixed Asset - _TC", + 0.0, + 60000.0, + ), + ( + "_Test Gain/Loss on Asset Disposal - _TC", + 0.0, + 17742.47, + ), + ("Debtors - _TC", 40000.0, 0.0), + ) + + gle = frappe.db.sql( + """select account, debit, credit from `tabGL Entry` + where voucher_type='Sales Invoice' and voucher_no = %s + order by account""", + si.name, + ) + + self.assertSequenceEqual(gle, expected_gle) + def test_asset_with_maintenance_required_status_after_sale(self): asset = create_asset( calculate_depreciation=1, diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 5b910dbb2e..789ca6c5ee 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -443,6 +443,7 @@ class AssetCapitalization(StockController): item.get("finance_book") or self.get("finance_book"), self.get("doctype"), self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", self.posting_date) 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 ad5ec3d6ed..8b359cd800 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -252,7 +252,10 @@ class AssetDepreciationSchedule(Document): # if asset is being sold or scrapped if date_of_disposal: - from_date = asset_doc.available_for_use_date + from_date = add_months( + getdate(asset_doc.available_for_use_date), + (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), + ) if self.depreciation_schedule: from_date = self.depreciation_schedule[-1].schedule_date From c1f1a033c97bc690e5857a964db9a8f2d2f152ef Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Wed, 24 May 2023 12:45:00 +0530 Subject: [PATCH 43/44] fix: tab-uniformity (#35400) Made Doctype tabs uniform Co-authored-by: Sagar Sharma --- .../doctype/purchase_order/purchase_order.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index c51c6edf1d..645abf25a8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -157,7 +157,7 @@ "party_account_currency", "inter_company_order_reference", "is_old_subcontracting_flow", - "dashboard" + "connections_tab" ], "fields": [ { @@ -1171,7 +1171,6 @@ "depends_on": "is_internal_supplier", "fieldname": "set_from_warehouse", "fieldtype": "Link", - "ignore_user_permissions": 1, "label": "Set From Warehouse", "options": "Warehouse" }, @@ -1185,12 +1184,6 @@ "fieldtype": "Tab Break", "label": "More Info" }, - { - "fieldname": "dashboard", - "fieldtype": "Tab Break", - "label": "Dashboard", - "show_dashboard": 1 - }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -1266,13 +1259,19 @@ "fieldname": "shipping_address_section", "fieldtype": "Section Break", "label": "Shipping Address" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2023-05-07 20:18:09.196799", + "modified": "2023-05-24 11:16:41.195340", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", From 8e3463c4ef4ac649fe12b91ee3ab7de11a680426 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 May 2023 14:12:58 +0530 Subject: [PATCH 44/44] fix: available qty not fetching for raw material in PP --- .../doctype/production_plan/production_plan.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index ab7aa52bb7..45a59cf732 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -451,10 +451,14 @@ frappe.ui.form.on("Material Request Plan Item", { for_warehouse: row.warehouse }, callback: function(r) { - let {projected_qty, actual_qty} = r.message; + if (r.message) { + let {projected_qty, actual_qty} = r.message[0]; - frappe.model.set_value(cdt, cdn, 'projected_qty', projected_qty); - frappe.model.set_value(cdt, cdn, 'actual_qty', actual_qty); + frappe.model.set_value(cdt, cdn, { + 'projected_qty': projected_qty, + 'actual_qty': actual_qty + }); + } } }) }