From 9119b4c5386b489fbdd343288089e55b21b77771 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 28 Jul 2020 08:49:44 +0530 Subject: [PATCH] fix: Unable to submit backdated stock transactions for different items (#22648) * fix: Unable to submit backdated stock transactions for different items * fix: Test cases * fix: Test Cases * fix: Test Cases * fix: Test for stock account JV * fix: Journal Entry Test --- .../doctype/coupon_code/test_coupon_code.py | 27 +++++++----- .../journal_entry/test_journal_entry.py | 42 +++++++++++++++--- .../doctype/pos_invoice/test_pos_invoice.py | 44 ++++++++++++------- erpnext/accounts/general_ledger.py | 5 ++- erpnext/stock/doctype/item/item.py | 4 +- .../doctype/stock_entry/test_stock_entry.py | 26 ++++++++--- 6 files changed, 102 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py index 990b896fde..3a0d4162ae 100644 --- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -26,22 +26,22 @@ def test_create_test_data(): "item_group": "_Test Item Group", "item_name": "_Test Tesla Car", "apply_warehouse_wise_reorder_level": 0, - "warehouse":"_Test Warehouse - _TC", + "warehouse":"Stores - TCP1", "gst_hsn_code": "999800", "valuation_rate": 5000, "standard_rate":5000, "item_defaults": [{ - "company": "_Test Company", - "default_warehouse": "_Test Warehouse - _TC", + "company": "_Test Company with perpetual inventory", + "default_warehouse": "Stores - TCP1", "default_price_list":"_Test Price List", - "expense_account": "_Test Account Cost for Goods Sold - _TC", - "buying_cost_center": "_Test Cost Center - _TC", - "selling_cost_center": "_Test Cost Center - _TC", - "income_account": "Sales - _TC" + "expense_account": "Cost of Goods Sold - TCP1", + "buying_cost_center": "Main - TCP1", + "selling_cost_center": "Main - TCP1", + "income_account": "Sales - TCP1" }], "show_in_website": 1, "route":"-test-tesla-car", - "website_warehouse": "_Test Warehouse - _TC" + "website_warehouse": "Stores - TCP1" }) item.insert() # create test item price @@ -63,12 +63,12 @@ def test_create_test_data(): "items": [{ "item_code": "_Test Tesla Car" }], - "warehouse":"_Test Warehouse - _TC", + "warehouse":"Stores - TCP1", "coupon_code_based":1, "selling": 1, "rate_or_discount": "Discount Percentage", "discount_percentage": 30, - "company": "_Test Company", + "company": "_Test Company with perpetual inventory", "currency":"INR", "for_price_list":"_Test Price List" }) @@ -112,7 +112,10 @@ class TestCouponCode(unittest.TestCase): self.assertEqual(coupon_code.get("used"),0) def test_2_sales_order_with_coupon_code(self): - so = make_sales_order(customer="_Test Customer",selling_price_list="_Test Price List",item_code="_Test Tesla Car", rate=5000,qty=1, do_not_submit=True) + so = make_sales_order(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', + customer="_Test Customer", selling_price_list="_Test Price List", item_code="_Test Tesla Car", rate=5000,qty=1, + do_not_submit=True) + so = frappe.get_doc('Sales Order', so.name) # check item price before coupon code is applied self.assertEqual(so.items[0].rate, 5000) @@ -120,7 +123,7 @@ class TestCouponCode(unittest.TestCase): so.sales_partner='_Test Coupon Partner' so.save() # check item price after coupon code is applied - self.assertEqual(so.items[0].rate, 3500) + self.assertEqual(so.items[0].rate, 3500) so.submit() def test_3_check_coupon_code_used_after_so(self): diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 23ad1eef14..479d4b64bb 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -6,6 +6,7 @@ import unittest, frappe from frappe.utils import flt, nowdate from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.exceptions import InvalidAccountCurrency +from erpnext.accounts.general_ledger import StockAccountInvalidTransaction class TestJournalEntry(unittest.TestCase): def test_journal_entry_with_against_jv(self): @@ -81,19 +82,46 @@ class TestJournalEntry(unittest.TestCase): from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory set_perpetual_inventory() - jv = frappe.copy_doc(test_records[0]) + jv = frappe.copy_doc({ + "cheque_date": nowdate(), + "cheque_no": "33", + "company": "_Test Company with perpetual inventory", + "doctype": "Journal Entry", + "accounts": [ + { + "account": "Debtors - TCP1", + "party_type": "Customer", + "party": "_Test Customer", + "credit_in_account_currency": 400.0, + "debit_in_account_currency": 0.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + "cost_center": "Main - TCP1" + }, + { + "account": "_Test Bank - TCP1", + "credit_in_account_currency": 0.0, + "debit_in_account_currency": 400.0, + "doctype": "Journal Entry Account", + "parentfield": "accounts", + "cost_center": "Main - TCP1" + } + ], + "naming_series": "_T-Journal Entry-", + "posting_date": nowdate(), + "user_remark": "test", + "voucher_type": "Bank Entry" + }) + jv.get("accounts")[0].update({ - "account": get_inventory_account('_Test Company'), - "company": "_Test Company", + "account": get_inventory_account('_Test Company with perpetual inventory'), + "company": "_Test Company with perpetual inventory", "party_type": None, "party": None }) - jv.insert() - - from erpnext.accounts.general_ledger import StockAccountInvalidTransaction self.assertRaises(StockAccountInvalidTransaction, jv.submit) - + jv.cancel() set_perpetual_inventory(0) def test_multi_currency(self): diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index f29572542c..9c62a87677 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -23,13 +23,13 @@ class TestPOSInvoice(unittest.TestCase): import time time.sleep(1) self.assertRaises(frappe.TimestampMismatchError, w2.save) - + def test_change_naming_series(self): inv = create_pos_invoice(do_not_submit=1) inv.naming_series = 'TEST-' self.assertRaises(frappe.CannotChangeConstantError, inv.save) - + def test_discount_and_inclusive_tax(self): inv = create_pos_invoice(qty=100, rate=50, do_not_save=1) inv.append("taxes", { @@ -66,7 +66,7 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(inv.net_total, 4298.25) self.assertEqual(inv.grand_total, 4900.00) - + def test_tax_calculation_with_multiple_items(self): inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True) item_row = inv.get("items")[0] @@ -148,7 +148,7 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(inv.grand_total, 5675.57) self.assertEqual(inv.rounding_adjustment, 0.43) self.assertEqual(inv.rounded_total, 5676.0) - + def test_tax_calculation_with_multiple_items_and_discount(self): inv = create_pos_invoice(qty=1, rate=75, do_not_save=True) item_row = inv.get("items")[0] @@ -194,7 +194,7 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(pos_return.get('payments')[0].amount, -500) self.assertEqual(pos_return.get('payments')[1].amount, -500) - + def test_pos_change_amount(self): pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC", income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105, @@ -208,33 +208,43 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(pos.grand_total, 105.0) self.assertEqual(pos.change_amount, 5.0) - + def test_without_payment(self): inv = create_pos_invoice(do_not_save=1) # Check that the invoice cannot be submitted without payments inv.payments = [] self.assertRaises(frappe.ValidationError, inv.insert) - + def test_serialized_item_transaction(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - se = make_serialized_item(target_warehouse="_Test Warehouse - _TC") + se = make_serialized_item(company='_Test Company with perpetual inventory', + target_warehouse="Stores - TCP1", cost_center='Main - TCP1', expense_account='Cost of Goods Sold - TCP1') + serial_nos = get_serial_nos(se.get("items")[0].serial_no) - pos = create_pos_invoice(item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1', + account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1', + expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1', + item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos.get("items")[0].serial_no = serial_nos[0] - pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) + pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000}) pos.insert() pos.submit() - pos2 = create_pos_invoice(item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos2 = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1', + account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1', + expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1', + item=se.get("items")[0].item_code, rate=1000, do_not_save=1) + pos2.get("items")[0].serial_no = serial_nos[0] - pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000}) - + pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000}) + self.assertRaises(frappe.ValidationError, pos2.insert) - + def test_loyalty_points(self): from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points @@ -255,14 +265,14 @@ class TestPOSInvoice(unittest.TestCase): inv.cancel() after_cancel_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program) self.assertEqual(after_cancel_lp_details.loyalty_points, before_lp_details.loyalty_points) - + def test_loyalty_points_redeemption(self): from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points # add 10 loyalty points create_pos_invoice(customer="Test Loyalty Customer", rate=10000) before_lp_details = get_loyalty_program_details_with_points("Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty") - + inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1) inv.redeem_loyalty_points = 1 inv.loyalty_points = before_lp_details.loyalty_points @@ -299,7 +309,7 @@ def create_pos_invoice(**args): pos_inv.return_against = args.return_against pos_inv.currency=args.currency or "INR" pos_inv.conversion_rate = args.conversion_rate or 1 - pos_inv.account_for_change_amount = "Cash - _TC" + pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC" pos_inv.append("items", { "item_code": args.item or args.item_code or "_Test Item", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a245d63f52..cf3deb828f 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -158,8 +158,10 @@ def validate_account_for_perpetual_inventory(gl_map): if account not in aii_accounts: continue + # Always use current date to get stock and account balance as there can future entries for + # other items account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account, - gl_map[0].posting_date, gl_map[0].company) + getdate(), gl_map[0].company) if gl_map[0].voucher_type=="Journal Entry": # In case of Journal Entry, there are no corresponding SL entries, @@ -169,7 +171,6 @@ def validate_account_for_perpetual_inventory(gl_map): frappe.throw(_("Account: {0} can only be updated via Stock Transactions") .format(account), StockAccountInvalidTransaction) - # This has been comment for a temporary, will add this code again on release of immutable ledger elif account_bal != stock_bal: precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency")) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index d5f479ff82..d7b43bf399 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -13,7 +13,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError, from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for) from frappe import _, msgprint from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate, - now_datetime, random_string, strip, get_link_to_form) + now_datetime, random_string, strip, get_link_to_form, nowtime) from frappe.utils.html_utils import clean_html from frappe.website.doctype.website_slideshow.website_slideshow import \ get_slideshow @@ -194,7 +194,7 @@ class Item(WebsiteGenerator): if default_warehouse: stock_entry = make_stock_entry(item_code=self.name, target=default_warehouse, qty=self.opening_stock, - rate=self.valuation_rate, company=default.company) + rate=self.valuation_rate, company=default.company, posting_date=getdate(), posting_time=nowtime()) stock_entry.add_comment("Comment", _("Opening Stock")) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 0fbc63101e..8e25804511 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -413,7 +413,7 @@ class TestStockEntry(unittest.TestCase): def test_serial_item_error(self): se, serial_nos = self.test_serial_by_series() if not frappe.db.exists('Serial No', 'ABCD'): - make_serialized_item("_Test Serialized Item", "ABCD\nEFGH") + make_serialized_item(item_code="_Test Serialized Item", serial_no="ABCD\nEFGH") se = frappe.copy_doc(test_records[0]) se.purpose = "Material Transfer" @@ -823,15 +823,29 @@ class TestStockEntry(unittest.TestCase): ]) ) -def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None): +def make_serialized_item(**args): + args = frappe._dict(args) se = frappe.copy_doc(test_records[0]) - se.get("items")[0].item_code = item_code or "_Test Serialized Item With Series" - se.get("items")[0].serial_no = serial_no + + if args.company: + se.company = args.company + + se.get("items")[0].item_code = args.item_code or "_Test Serialized Item With Series" + + if args.serial_no: + se.get("items")[0].serial_no = args.serial_no + + if args.cost_center: + se.get("items")[0].cost_center = args.cost_center + + if args.expense_account: + se.get("items")[0].expense_account = args.expense_account + se.get("items")[0].qty = 2 se.get("items")[0].transfer_qty = 2 - if target_warehouse: - se.get("items")[0].t_warehouse = target_warehouse + if args.target_warehouse: + se.get("items")[0].t_warehouse = args.target_warehouse se.set_stock_entry_type() se.insert()