mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 08:54:45 +00:00
test: Internal tranfer precision loss test
(cherry picked from commit dc20b21fb5)
# Conflicts:
# erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
This commit is contained in:
@@ -31,10 +31,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
|
|||||||
get_qty_after_transaction,
|
get_qty_after_transaction,
|
||||||
make_stock_entry,
|
make_stock_entry,
|
||||||
)
|
)
|
||||||
from erpnext.stock.utils import get_incoming_rate
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
|
create_stock_reconciliation,
|
||||||
|
)
|
||||||
|
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
|
||||||
|
|
||||||
|
|
||||||
class TestSalesInvoice(unittest.TestCase):
|
class TestSalesInvoice(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items
|
||||||
|
|
||||||
|
create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}])
|
||||||
|
create_internal_parties()
|
||||||
|
setup_accounts()
|
||||||
|
|
||||||
def make(self):
|
def make(self):
|
||||||
w = frappe.copy_doc(test_records[0])
|
w = frappe.copy_doc(test_records[0])
|
||||||
w.is_pos = 0
|
w.is_pos = 0
|
||||||
@@ -1687,7 +1697,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.save()
|
si.save()
|
||||||
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate))
|
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate))
|
||||||
|
|
||||||
def test_outstanding_amount_after_advance_jv_cancelation(self):
|
def test_outstanding_amount_after_advance_jv_cancellation(self):
|
||||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
|
||||||
test_records as jv_test_records,
|
test_records as jv_test_records,
|
||||||
)
|
)
|
||||||
@@ -1731,7 +1741,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")),
|
flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_outstanding_amount_after_advance_payment_entry_cancelation(self):
|
def test_outstanding_amount_after_advance_payment_entry_cancellation(self):
|
||||||
pe = frappe.get_doc(
|
pe = frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Payment Entry",
|
"doctype": "Payment Entry",
|
||||||
@@ -2369,29 +2379,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
acc_settings.save()
|
acc_settings.save()
|
||||||
|
|
||||||
def test_inter_company_transaction(self):
|
def test_inter_company_transaction(self):
|
||||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
|
||||||
|
|
||||||
create_internal_customer(
|
|
||||||
customer_name="_Test Internal Customer",
|
|
||||||
represents_company="_Test Company 1",
|
|
||||||
allowed_to_interact_with="Wind Power LLC",
|
|
||||||
)
|
|
||||||
|
|
||||||
if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
|
|
||||||
supplier = frappe.get_doc(
|
|
||||||
{
|
|
||||||
"supplier_group": "_Test Supplier Group",
|
|
||||||
"supplier_name": "_Test Internal Supplier",
|
|
||||||
"doctype": "Supplier",
|
|
||||||
"is_internal_supplier": 1,
|
|
||||||
"represents_company": "Wind Power LLC",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
supplier.append("companies", {"company": "_Test Company 1"})
|
|
||||||
|
|
||||||
supplier.insert()
|
|
||||||
|
|
||||||
si = create_sales_invoice(
|
si = create_sales_invoice(
|
||||||
company="Wind Power LLC",
|
company="Wind Power LLC",
|
||||||
customer="_Test Internal Customer",
|
customer="_Test Internal Customer",
|
||||||
@@ -2420,6 +2407,69 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
def test_inter_company_transaction_without_default_warehouse(self):
|
||||||
|
"Check mapping (expense account) of inter company SI to PI in absence of default warehouse."
|
||||||
|
# setup
|
||||||
|
old_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
|
||||||
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
||||||
|
|
||||||
|
old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled("_Test Company 1")
|
||||||
|
frappe.local.enable_perpetual_inventory["_Test Company 1"] = 1
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Company",
|
||||||
|
"_Test Company 1",
|
||||||
|
"stock_received_but_not_billed",
|
||||||
|
"Stock Received But Not Billed - _TC1",
|
||||||
|
)
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Company",
|
||||||
|
"_Test Company 1",
|
||||||
|
"expenses_included_in_valuation",
|
||||||
|
"Expenses Included In Valuation - _TC1",
|
||||||
|
)
|
||||||
|
|
||||||
|
# begin test
|
||||||
|
si = create_sales_invoice(
|
||||||
|
company="Wind Power LLC",
|
||||||
|
customer="_Test Internal Customer",
|
||||||
|
debit_to="Debtors - WP",
|
||||||
|
warehouse="Stores - WP",
|
||||||
|
income_account="Sales - WP",
|
||||||
|
expense_account="Cost of Goods Sold - WP",
|
||||||
|
cost_center="Main - WP",
|
||||||
|
currency="USD",
|
||||||
|
update_stock=1,
|
||||||
|
do_not_save=1,
|
||||||
|
)
|
||||||
|
si.selling_price_list = "_Test Price List Rest of the World"
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
|
||||||
|
|
||||||
|
# in absence of warehouse Stock Received But Not Billed is set as expense account while mapping
|
||||||
|
# mapping is not obstructed
|
||||||
|
self.assertIsNone(target_doc.items[0].warehouse)
|
||||||
|
self.assertEqual(target_doc.items[0].expense_account, "Stock Received But Not Billed - _TC1")
|
||||||
|
|
||||||
|
target_doc.items[0].update({"cost_center": "Main - _TC1"})
|
||||||
|
|
||||||
|
# missing warehouse is validated on save, after mapping
|
||||||
|
self.assertRaises(WarehouseMissingError, target_doc.save)
|
||||||
|
|
||||||
|
target_doc.items[0].update({"warehouse": "Stores - _TC1"})
|
||||||
|
target_doc.save()
|
||||||
|
|
||||||
|
# after warehouse is set, linked account or default inventory account is set
|
||||||
|
self.assertEqual(target_doc.items[0].expense_account, "Stock In Hand - _TC1")
|
||||||
|
|
||||||
|
# tear down
|
||||||
|
frappe.local.enable_perpetual_inventory["_Test Company 1"] = old_perpetual_inventory
|
||||||
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
|
||||||
|
|
||||||
|
>>>>>>> dc20b21fb5 (test: Internal tranfer precision loss test)
|
||||||
def test_sle_for_target_warehouse(self):
|
def test_sle_for_target_warehouse(self):
|
||||||
se = make_stock_entry(
|
se = make_stock_entry(
|
||||||
item_code="138-CMS Shoe",
|
item_code="138-CMS Shoe",
|
||||||
@@ -2451,34 +2501,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
se.cancel()
|
se.cancel()
|
||||||
|
|
||||||
def test_internal_transfer_gl_entry(self):
|
def test_internal_transfer_gl_entry(self):
|
||||||
## Create internal transfer account
|
|
||||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
|
||||||
|
|
||||||
account = create_account(
|
|
||||||
account_name="Unrealized Profit",
|
|
||||||
parent_account="Current Liabilities - TCP1",
|
|
||||||
company="_Test Company with perpetual inventory",
|
|
||||||
)
|
|
||||||
|
|
||||||
frappe.db.set_value(
|
|
||||||
"Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account
|
|
||||||
)
|
|
||||||
|
|
||||||
customer = create_internal_customer(
|
|
||||||
"_Test Internal Customer 2",
|
|
||||||
"_Test Company with perpetual inventory",
|
|
||||||
"_Test Company with perpetual inventory",
|
|
||||||
)
|
|
||||||
|
|
||||||
create_internal_supplier(
|
|
||||||
"_Test Internal Supplier 2",
|
|
||||||
"_Test Company with perpetual inventory",
|
|
||||||
"_Test Company with perpetual inventory",
|
|
||||||
)
|
|
||||||
|
|
||||||
si = create_sales_invoice(
|
si = create_sales_invoice(
|
||||||
company="_Test Company with perpetual inventory",
|
company="_Test Company with perpetual inventory",
|
||||||
customer=customer,
|
customer="_Test Internal Customer 2",
|
||||||
debit_to="Debtors - TCP1",
|
debit_to="Debtors - TCP1",
|
||||||
warehouse="Stores - TCP1",
|
warehouse="Stores - TCP1",
|
||||||
income_account="Sales - TCP1",
|
income_account="Sales - TCP1",
|
||||||
@@ -2492,7 +2517,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.update_stock = 1
|
si.update_stock = 1
|
||||||
si.items[0].target_warehouse = "Work In Progress - TCP1"
|
si.items[0].target_warehouse = "Work In Progress - TCP1"
|
||||||
|
|
||||||
# Add stock to stores for succesful stock transfer
|
# Add stock to stores for successful stock transfer
|
||||||
make_stock_entry(
|
make_stock_entry(
|
||||||
target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100
|
target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100
|
||||||
)
|
)
|
||||||
@@ -2548,6 +2573,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
def test_eway_bill_json(self):
|
def test_eway_bill_json(self):
|
||||||
si = make_sales_invoice_for_ewaybill()
|
si = make_sales_invoice_for_ewaybill()
|
||||||
|
|
||||||
@@ -2829,6 +2855,78 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(einvoice["ItemList"][1]["Discount"], 0)
|
self.assertEqual(einvoice["ItemList"][1]["Discount"], 0)
|
||||||
self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 20)
|
self.assertEqual(einvoice["ItemList"][1]["UnitPrice"], 20)
|
||||||
|
=======
|
||||||
|
def test_internal_transfer_gl_precision_issues(self):
|
||||||
|
# Make a stock queue of an item with two valuations
|
||||||
|
|
||||||
|
# Remove all existing stock for this
|
||||||
|
if get_stock_balance("_Test Internal Transfer Item", "Stores - TCP1", "2022-04-10"):
|
||||||
|
create_stock_reconciliation(
|
||||||
|
item_code="_Test Internal Transfer Item",
|
||||||
|
warehouse="Stores - TCP1",
|
||||||
|
qty=0,
|
||||||
|
rate=0,
|
||||||
|
company="_Test Company with perpetual inventory",
|
||||||
|
expense_account="Stock Adjustment - TCP1"
|
||||||
|
if frappe.get_all("Stock Ledger Entry")
|
||||||
|
else "Temporary Opening - TCP1",
|
||||||
|
posting_date="2020-04-10",
|
||||||
|
posting_time="14:00",
|
||||||
|
)
|
||||||
|
|
||||||
|
make_stock_entry(
|
||||||
|
item_code="_Test Internal Transfer Item",
|
||||||
|
target="Stores - TCP1",
|
||||||
|
qty=9000000,
|
||||||
|
basic_rate=52.0,
|
||||||
|
posting_date="2020-04-10",
|
||||||
|
posting_time="14:00",
|
||||||
|
)
|
||||||
|
make_stock_entry(
|
||||||
|
item_code="_Test Internal Transfer Item",
|
||||||
|
target="Stores - TCP1",
|
||||||
|
qty=60000000,
|
||||||
|
basic_rate=52.349777,
|
||||||
|
posting_date="2020-04-10",
|
||||||
|
posting_time="14:00",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make an internal transfer Sales Invoice Stock in non stock uom to check
|
||||||
|
# for rounding errors while converting to stock uom
|
||||||
|
si = create_sales_invoice(
|
||||||
|
company="_Test Company with perpetual inventory",
|
||||||
|
customer="_Test Internal Customer 2",
|
||||||
|
item_code="_Test Internal Transfer Item",
|
||||||
|
qty=5000000,
|
||||||
|
uom="Box",
|
||||||
|
debit_to="Debtors - TCP1",
|
||||||
|
warehouse="Stores - TCP1",
|
||||||
|
income_account="Sales - TCP1",
|
||||||
|
expense_account="Cost of Goods Sold - TCP1",
|
||||||
|
cost_center="Main - TCP1",
|
||||||
|
currency="INR",
|
||||||
|
do_not_save=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check GL Entries with precision
|
||||||
|
si.update_stock = 1
|
||||||
|
si.items[0].target_warehouse = "Work In Progress - TCP1"
|
||||||
|
si.items[0].conversion_factor = 10
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
# Check if adjustment entry is created
|
||||||
|
self.assertTrue(
|
||||||
|
frappe.db.exists(
|
||||||
|
"GL Entry",
|
||||||
|
{
|
||||||
|
"voucher_type": "Sales Invoice",
|
||||||
|
"voucher_no": si.name,
|
||||||
|
"remarks": "Rounding gain/loss Entry for Stock Transfer",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
>>>>>>> dc20b21fb5 (test: Internal tranfer precision loss test)
|
||||||
|
|
||||||
def test_item_tax_net_range(self):
|
def test_item_tax_net_range(self):
|
||||||
item = create_item("T Shirt")
|
item = create_item("T Shirt")
|
||||||
@@ -3278,7 +3376,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
[deferred_account, 2022.47, 0.0, "2019-03-15"],
|
[deferred_account, 2022.47, 0.0, "2019-03-15"],
|
||||||
]
|
]
|
||||||
|
|
||||||
gl_entries = gl_entries = frappe.db.sql(
|
gl_entries = frappe.db.sql(
|
||||||
"""select account, debit, credit, posting_date
|
"""select account, debit, credit, posting_date
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
|
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
|
||||||
@@ -3809,6 +3907,34 @@ def get_taxes_and_charges():
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def create_internal_parties():
|
||||||
|
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||||
|
|
||||||
|
create_internal_customer(
|
||||||
|
customer_name="_Test Internal Customer",
|
||||||
|
represents_company="_Test Company 1",
|
||||||
|
allowed_to_interact_with="Wind Power LLC",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_internal_customer(
|
||||||
|
customer_name="_Test Internal Customer 2",
|
||||||
|
represents_company="_Test Company with perpetual inventory",
|
||||||
|
allowed_to_interact_with="_Test Company with perpetual inventory",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_internal_supplier(
|
||||||
|
supplier_name="_Test Internal Supplier",
|
||||||
|
represents_company="Wind Power LLC",
|
||||||
|
allowed_to_interact_with="_Test Company 1",
|
||||||
|
)
|
||||||
|
|
||||||
|
create_internal_supplier(
|
||||||
|
supplier_name="_Test Internal Supplier 2",
|
||||||
|
represents_company="_Test Company with perpetual inventory",
|
||||||
|
allowed_to_interact_with="_Test Company with perpetual inventory",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
||||||
if not frappe.db.exists("Supplier", supplier_name):
|
if not frappe.db.exists("Supplier", supplier_name):
|
||||||
supplier = frappe.get_doc(
|
supplier = frappe.get_doc(
|
||||||
@@ -3831,6 +3957,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter
|
|||||||
return supplier_name
|
return supplier_name
|
||||||
|
|
||||||
|
|
||||||
|
def setup_accounts():
|
||||||
|
## Create internal transfer account
|
||||||
|
account = create_account(
|
||||||
|
account_name="Unrealized Profit",
|
||||||
|
parent_account="Current Liabilities - TCP1",
|
||||||
|
company="_Test Company with perpetual inventory",
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value(
|
||||||
|
"Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_taxes(doc):
|
def add_taxes(doc):
|
||||||
doc.append(
|
doc.append(
|
||||||
"taxes",
|
"taxes",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ test_ignore = ["BOM"]
|
|||||||
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
|
test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
|
||||||
|
|
||||||
|
|
||||||
def make_item(item_code=None, properties=None):
|
def make_item(item_code=None, properties=None, uoms=None):
|
||||||
if not item_code:
|
if not item_code:
|
||||||
item_code = frappe.generate_hash(length=16)
|
item_code = frappe.generate_hash(length=16)
|
||||||
|
|
||||||
@@ -54,6 +54,11 @@ def make_item(item_code=None, properties=None):
|
|||||||
for item_default in [doc for doc in item.get("item_defaults") if not doc.default_warehouse]:
|
for item_default in [doc for doc in item.get("item_defaults") if not doc.default_warehouse]:
|
||||||
item_default.default_warehouse = "_Test Warehouse - _TC"
|
item_default.default_warehouse = "_Test Warehouse - _TC"
|
||||||
item_default.company = "_Test Company"
|
item_default.company = "_Test Company"
|
||||||
|
|
||||||
|
if uoms:
|
||||||
|
for uom in uoms:
|
||||||
|
item.append("uoms", uom)
|
||||||
|
|
||||||
item.insert()
|
item.insert()
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|||||||
@@ -902,13 +902,15 @@ def create_product_bundle_item(new_item_code, packed_items):
|
|||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
|
|
||||||
def create_items():
|
def create_items(items=None, uoms=None):
|
||||||
items = [
|
if not items:
|
||||||
"_Test Item for Reposting",
|
items = [
|
||||||
"_Test Finished Item for Reposting",
|
"_Test Item for Reposting",
|
||||||
"_Test Subcontracted Item for Reposting",
|
"_Test Finished Item for Reposting",
|
||||||
"_Test Bundled Item for Reposting",
|
"_Test Subcontracted Item for Reposting",
|
||||||
]
|
"_Test Bundled Item for Reposting",
|
||||||
|
]
|
||||||
|
|
||||||
for d in items:
|
for d in items:
|
||||||
properties = {"valuation_method": "FIFO"}
|
properties = {"valuation_method": "FIFO"}
|
||||||
if d == "_Test Bundled Item for Reposting":
|
if d == "_Test Bundled Item for Reposting":
|
||||||
@@ -916,7 +918,7 @@ def create_items():
|
|||||||
elif d == "_Test Subcontracted Item for Reposting":
|
elif d == "_Test Subcontracted Item for Reposting":
|
||||||
properties.update({"is_sub_contracted_item": 1})
|
properties.update({"is_sub_contracted_item": 1})
|
||||||
|
|
||||||
make_item(d, properties=properties)
|
make_item(d, properties=properties, uoms=uoms)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,9 @@ class StockReconciliation(StockController):
|
|||||||
key.append(row.get(field))
|
key.append(row.get(field))
|
||||||
|
|
||||||
if key in item_warehouse_combinations:
|
if key in item_warehouse_combinations:
|
||||||
self.validation_messages.append(_get_msg(row_num, _("Duplicate entry")))
|
self.validation_messages.append(
|
||||||
|
_get_msg(row_num, _("Same item and warehouse combination already entered."))
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
item_warehouse_combinations.append(key)
|
item_warehouse_combinations.append(key)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user