From 38aaba5720b28d0360339b5fb87d955eab392f6f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 13 May 2023 13:00:05 +0530 Subject: [PATCH] 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" + ), + } + )