mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-28 06:08:25 +00:00
test: improve ERPNext test isolation and runtime (#58507)
This commit is contained in:
@@ -4,29 +4,18 @@
|
||||
import frappe
|
||||
from frappe.utils import add_months, getdate
|
||||
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
from erpnext.accounts.doctype.mode_of_payment.test_mode_of_payment import (
|
||||
set_default_account_for_mode_of_payment,
|
||||
)
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
from erpnext.tests.utils import ERPNextTestSuite, if_lending_app_installed, if_lending_app_not_installed
|
||||
|
||||
|
||||
class TestBankClearance(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
frappe.clear_cache()
|
||||
create_warehouse(
|
||||
warehouse_name="_Test Warehouse",
|
||||
properties={"parent_warehouse": "All Warehouses - _TC"},
|
||||
company="_Test Company",
|
||||
)
|
||||
create_item("_Test Item")
|
||||
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company")
|
||||
|
||||
make_bank_account()
|
||||
add_transactions()
|
||||
|
||||
@@ -139,11 +128,8 @@ def add_transactions():
|
||||
|
||||
|
||||
def make_payment_entry():
|
||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||
|
||||
supplier = create_supplier(supplier_name="_Test Supplier")
|
||||
pi = make_purchase_invoice(
|
||||
supplier=supplier.name,
|
||||
supplier="_Test Supplier",
|
||||
supplier_warehouse="_Test Warehouse - _TC",
|
||||
expense_account="Cost of Goods Sold - _TC",
|
||||
uom="Nos",
|
||||
@@ -158,10 +144,6 @@ def make_payment_entry():
|
||||
|
||||
|
||||
def make_pos_sales_invoice():
|
||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
|
||||
make_customer,
|
||||
)
|
||||
|
||||
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
|
||||
|
||||
if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
|
||||
@@ -170,13 +152,13 @@ def make_pos_sales_invoice():
|
||||
)
|
||||
mode_of_payment.save()
|
||||
|
||||
customer = make_customer(customer="_Test Customer")
|
||||
|
||||
mode_of_payment = frappe.get_doc("Mode of Payment", "Wire Transfer")
|
||||
|
||||
set_default_account_for_mode_of_payment(mode_of_payment, "_Test Company", "_Test Bank Clearance - _TC")
|
||||
|
||||
si = create_sales_invoice(customer=customer, item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1)
|
||||
si = create_sales_invoice(
|
||||
customer="_Test Customer", item="_Test Item", is_pos=1, qty=1, rate=1000, do_not_save=1
|
||||
)
|
||||
si.set("payments", [])
|
||||
si.append("payments", {"mode_of_payment": "Wire Transfer", "amount": 1000})
|
||||
si.insert()
|
||||
|
||||
@@ -23,8 +23,6 @@ from erpnext.tests.utils import ERPNextTestSuite, if_lending_app_installed
|
||||
|
||||
class TestBankTransaction(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
make_pos_profile()
|
||||
|
||||
# generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error
|
||||
uniq_identifier = frappe.generate_hash(length=10)
|
||||
gl_account = create_gl_account("_Test Bank " + uniq_identifier)
|
||||
@@ -32,6 +30,15 @@ class TestBankTransaction(ERPNextTestSuite):
|
||||
gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier
|
||||
)
|
||||
|
||||
if self._testMethodName in {
|
||||
"test_cancel_voucher",
|
||||
"test_clearance_date_cleared_on_amend",
|
||||
"test_reconcile",
|
||||
}:
|
||||
add_reconciliation_data(bank_account, gl_account)
|
||||
return
|
||||
|
||||
make_pos_profile()
|
||||
add_transactions(bank_account=bank_account)
|
||||
add_vouchers(gl_account=gl_account)
|
||||
|
||||
@@ -47,7 +54,7 @@ class TestBankTransaction(ERPNextTestSuite):
|
||||
from_date=bank_transaction.date,
|
||||
to_date=utils.today(),
|
||||
)
|
||||
self.assertEqual(linked_payments[0]["party"], "Conrad Electronic")
|
||||
self.assertIn("Conrad Electronic", [payment["party"] for payment in linked_payments])
|
||||
|
||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||
def test_reconcile(self):
|
||||
@@ -347,6 +354,36 @@ def add_transactions(bank_account="_Test Bank - _TC"):
|
||||
doc.submit()
|
||||
|
||||
|
||||
def add_reconciliation_data(bank_account, gl_account):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Bank Transaction",
|
||||
"description": "1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
|
||||
"date": "2018-10-23",
|
||||
"deposit": 1700,
|
||||
"currency": "INR",
|
||||
"bank_account": bank_account,
|
||||
}
|
||||
).insert()
|
||||
doc.submit()
|
||||
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_group": "All Supplier Groups",
|
||||
"supplier_type": "Company",
|
||||
"supplier_name": "Mr G",
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
|
||||
pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account)
|
||||
pe.reference_no = "Herr G Nov 18"
|
||||
pe.reference_date = "2018-11-01"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
|
||||
def add_vouchers(gl_account="_Test Bank - _TC"):
|
||||
try:
|
||||
frappe.get_doc(
|
||||
|
||||
@@ -31,11 +31,4 @@ class TestFinanceBook(ERPNextTestSuite):
|
||||
|
||||
|
||||
def create_finance_book():
|
||||
if not frappe.db.exists("Finance Book", "_Test Finance Book"):
|
||||
finance_book = frappe.get_doc(
|
||||
{"doctype": "Finance Book", "finance_book_name": "_Test Finance Book"}
|
||||
).insert()
|
||||
else:
|
||||
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
|
||||
|
||||
return finance_book
|
||||
return frappe.get_doc("Finance Book", "Test Finance Book 1")
|
||||
|
||||
@@ -318,9 +318,8 @@ class TestJournalEntry(ERPNextTestSuite):
|
||||
)
|
||||
|
||||
# the guard must not disclose the reversal to a user who cannot read the entry
|
||||
frappe.set_user("Guest")
|
||||
self.addCleanup(frappe.set_user, "Administrator")
|
||||
self.assertRaises(frappe.PermissionError, make_reverse_journal_entry, rjv.name)
|
||||
with self.set_user("Guest"):
|
||||
self.assertRaises(frappe.PermissionError, make_reverse_journal_entry, rjv.name)
|
||||
|
||||
def test_disallow_change_in_account_currency_for_a_party(self):
|
||||
# create jv in USD
|
||||
|
||||
@@ -789,7 +789,6 @@ class TestPaymentEntry(ERPNextTestSuite):
|
||||
company="_Test Company",
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "bank_charges_account", bank_charges_account)
|
||||
self.addCleanup(frappe.db.set_value, "Company", "_Test Company", "bank_charges_account", "")
|
||||
|
||||
pe = frappe.new_doc("Payment Entry")
|
||||
pe.payment_type = "Internal Transfer"
|
||||
@@ -834,7 +833,6 @@ class TestPaymentEntry(ERPNextTestSuite):
|
||||
company="_Test Company",
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "bank_charges_account", bank_charges_account)
|
||||
self.addCleanup(frappe.db.set_value, "Company", "_Test Company", "bank_charges_account", "")
|
||||
|
||||
pe = frappe.new_doc("Payment Entry")
|
||||
pe.payment_type = "Internal Transfer"
|
||||
@@ -1109,8 +1107,6 @@ class TestPaymentEntry(ERPNextTestSuite):
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "exchange_gain_account", gain_account)
|
||||
frappe.db.set_value("Company", "_Test Company", "exchange_loss_account", loss_account)
|
||||
self.addCleanup(frappe.db.set_value, "Company", "_Test Company", "exchange_gain_account", "")
|
||||
self.addCleanup(frappe.db.set_value, "Company", "_Test Company", "exchange_loss_account", "")
|
||||
|
||||
si_gain = create_sales_invoice(
|
||||
customer="_Test Customer USD",
|
||||
|
||||
@@ -201,8 +201,6 @@ class TestPaymentReconciliation(ERPNextTestSuite):
|
||||
)
|
||||
frappe.db.set_value("Company", self.company, "exchange_gain_account", gain_account)
|
||||
frappe.db.set_value("Company", self.company, "exchange_loss_account", loss_account)
|
||||
self.addCleanup(frappe.db.set_value, "Company", self.company, "exchange_gain_account", "")
|
||||
self.addCleanup(frappe.db.set_value, "Company", self.company, "exchange_loss_account", "")
|
||||
return gain_account, loss_account
|
||||
|
||||
def create_foreign_currency_sales_invoice(self, conversion_rate):
|
||||
@@ -1331,15 +1329,6 @@ class TestPaymentReconciliation(ERPNextTestSuite):
|
||||
test_user = "test@example.com"
|
||||
permitted_ccs = ["_Test Cost Center - _TC", "_Test Cost Center 2 - _TC"]
|
||||
restricted_cc = "_Test Write Off Cost Center - _TC"
|
||||
existing_apply_strict_user_permissions = cint(
|
||||
frappe.db.get_single_value("System Settings", "apply_strict_user_permissions")
|
||||
)
|
||||
self.addCleanup(
|
||||
frappe.db.set_single_value,
|
||||
"System Settings",
|
||||
"apply_strict_user_permissions",
|
||||
existing_apply_strict_user_permissions,
|
||||
)
|
||||
transaction_date = nowdate()
|
||||
rate = 100
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
PAYMENT_URL = "https://example.com/payment"
|
||||
SEND_EMAIL_MOCK = MagicMock(return_value=None)
|
||||
GET_PAYMENT_URL_MOCK = MagicMock(return_value=PAYMENT_URL)
|
||||
GET_PAYMENT_GATEWAY_CONTROLLER_MOCK = MagicMock()
|
||||
|
||||
payment_gateways = [
|
||||
{"doctype": "Payment Gateway", "gateway": "_Test Gateway"},
|
||||
@@ -71,6 +74,18 @@ payment_method = [
|
||||
]
|
||||
|
||||
|
||||
@patch(
|
||||
"erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email",
|
||||
new=SEND_EMAIL_MOCK,
|
||||
)
|
||||
@patch(
|
||||
"erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url",
|
||||
new=GET_PAYMENT_URL_MOCK,
|
||||
)
|
||||
@patch(
|
||||
"erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller",
|
||||
new=GET_PAYMENT_GATEWAY_CONTROLLER_MOCK,
|
||||
)
|
||||
class TestPaymentRequest(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
for payment_gateway in payment_gateways:
|
||||
@@ -89,24 +104,11 @@ class TestPaymentRequest(ERPNextTestSuite):
|
||||
):
|
||||
frappe.get_doc(method).insert(ignore_permissions=True)
|
||||
|
||||
send_email = patch(
|
||||
"erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.send_email",
|
||||
return_value=None,
|
||||
)
|
||||
self.send_email = send_email.start()
|
||||
self.addCleanup(send_email.stop)
|
||||
get_payment_url = patch(
|
||||
# this also shadows one (1) call to _get_payment_gateway_controller
|
||||
"erpnext.accounts.doctype.payment_request.payment_request.PaymentRequest.get_payment_url",
|
||||
return_value=PAYMENT_URL,
|
||||
)
|
||||
self.get_payment_url = get_payment_url.start()
|
||||
self.addCleanup(get_payment_url.stop)
|
||||
_get_payment_gateway_controller = patch(
|
||||
"erpnext.accounts.doctype.payment_request.payment_request._get_payment_gateway_controller",
|
||||
)
|
||||
self._get_payment_gateway_controller = _get_payment_gateway_controller.start()
|
||||
self.addCleanup(_get_payment_gateway_controller.stop)
|
||||
for mock in (SEND_EMAIL_MOCK, GET_PAYMENT_URL_MOCK, GET_PAYMENT_GATEWAY_CONTROLLER_MOCK):
|
||||
mock.reset_mock()
|
||||
self.send_email = SEND_EMAIL_MOCK
|
||||
self.get_payment_url = GET_PAYMENT_URL_MOCK
|
||||
self._get_payment_gateway_controller = GET_PAYMENT_GATEWAY_CONTROLLER_MOCK
|
||||
|
||||
def test_payment_request_linkings(self):
|
||||
so_inr = make_sales_order(currency="INR", do_not_save=True)
|
||||
|
||||
@@ -21,13 +21,12 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
init_user_and_profile()
|
||||
self.test_user, self.pos_profile = init_user_and_profile()
|
||||
make_stock_entry(target="_Test Warehouse - _TC", qty=2, basic_rate=100)
|
||||
frappe.db.set_single_value("POS Settings", "invoice_type", "POS Invoice")
|
||||
|
||||
def test_pos_closing_entry(self):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
|
||||
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
@@ -59,8 +58,7 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
"""
|
||||
Test if POS Closing Entry is created without item code
|
||||
"""
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv = create_pos_invoice(rate=3500, do_not_submit=1, item_name="Test Item", without_item_code=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
@@ -79,10 +77,9 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
"""
|
||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
test_item_qty = get_test_item_qty(pos_profile)
|
||||
test_item_qty = get_test_item_qty(self.pos_profile)
|
||||
|
||||
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
|
||||
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
@@ -104,13 +101,11 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
pcv_doc.flags.in_test = True
|
||||
pcv_doc.submit()
|
||||
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
test_item_qty_after_sales = get_test_item_qty(pos_profile)
|
||||
test_item_qty_after_sales = get_test_item_qty(self.pos_profile)
|
||||
self.assertEqual(test_item_qty_after_sales, test_item_qty - 1)
|
||||
|
||||
def test_cancelling_of_pos_closing_entry(self):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
|
||||
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
@@ -169,9 +164,7 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
pos_profile.insert()
|
||||
self.assertTrue(frappe.db.exists("POS Profile", pos_profile.name))
|
||||
|
||||
test_user = init_user_and_profile(do_not_create_pos_profile=1)
|
||||
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(pos_profile, self.test_user.name)
|
||||
pos_inv1 = create_pos_invoice(rate=350, do_not_submit=1, pos_profile=pos_profile.name)
|
||||
pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
|
||||
pos_inv1.save()
|
||||
@@ -195,9 +188,6 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
|
||||
def test_merging_into_sales_invoice_for_batched_item(self):
|
||||
frappe.flags.print_message = False
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.stock.doctype.batch.batch import get_batch_qty
|
||||
|
||||
item_doc = make_item(
|
||||
@@ -220,8 +210,7 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
)
|
||||
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv = create_pos_invoice(
|
||||
item_code=item_code,
|
||||
@@ -291,18 +280,17 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
|
||||
@ERPNextTestSuite.change_settings("POS Settings", {"invoice_type": "Sales Invoice"})
|
||||
def test_closing_entries_with_sales_invoice(self):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_si = create_sales_invoice(
|
||||
qty=10, is_created_using_pos=1, pos_profile=pos_profile.name, do_not_save=1
|
||||
qty=10, is_created_using_pos=1, pos_profile=self.pos_profile.name, do_not_save=1
|
||||
)
|
||||
pos_si.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000})
|
||||
pos_si.save()
|
||||
pos_si.submit()
|
||||
|
||||
pos_si2 = create_sales_invoice(
|
||||
qty=5, is_created_using_pos=1, pos_profile=pos_profile.name, do_not_save=11
|
||||
qty=5, is_created_using_pos=1, pos_profile=self.pos_profile.name, do_not_save=11
|
||||
)
|
||||
pos_si2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000})
|
||||
pos_si2.save()
|
||||
@@ -332,12 +320,10 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
"""
|
||||
from erpnext.accounts.doctype.sales_invoice.mapper import make_sales_return
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
|
||||
with self.change_settings("POS Settings", {"invoice_type": "Sales Invoice"}):
|
||||
opening_entry1 = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry1 = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_si1, pos_si2 = create_multiple_sales_invoices(pos_profile)
|
||||
pos_si1, pos_si2 = create_multiple_sales_invoices(self.pos_profile)
|
||||
|
||||
pos_inv = create_pos_invoice(rate=100, do_not_save=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
|
||||
@@ -357,13 +343,13 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
self.assertEqual(pos_si2.pos_closing_entry, pcv_doc1.name)
|
||||
|
||||
with self.change_settings("POS Settings", {"invoice_type": "POS Invoice"}):
|
||||
opening_entry2 = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry2 = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv1, pos_inv2 = create_multiple_pos_invoices(pos_profile)
|
||||
pos_inv1, pos_inv2 = create_multiple_pos_invoices(self.pos_profile)
|
||||
|
||||
# Trying to create Sales Invoice when invoice_type is set to POS Invoice.
|
||||
pos_si3 = create_sales_invoice(
|
||||
qty=1, is_created_using_pos=1, pos_profile=pos_profile.name, do_not_save=1
|
||||
qty=1, is_created_using_pos=1, pos_profile=self.pos_profile.name, do_not_save=1
|
||||
)
|
||||
pos_si3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
|
||||
self.assertRaises(frappe.ValidationError, pos_si3.save)
|
||||
@@ -394,16 +380,14 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
"""
|
||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
|
||||
with self.change_settings("POS Settings", {"invoice_type": "POS Invoice"}):
|
||||
opening_entry1 = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry1 = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_inv1, pos_inv2 = create_multiple_pos_invoices(pos_profile)
|
||||
pos_inv1, pos_inv2 = create_multiple_pos_invoices(self.pos_profile)
|
||||
|
||||
# Trying to create Sales Invoice when invoice_type is set to POS Invoice.
|
||||
pos_sinv = create_sales_invoice(
|
||||
qty=1, is_created_using_pos=1, pos_profile=pos_profile.name, do_not_save=1
|
||||
qty=1, is_created_using_pos=1, pos_profile=self.pos_profile.name, do_not_save=1
|
||||
)
|
||||
pos_sinv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
|
||||
self.assertRaises(frappe.ValidationError, pos_sinv.save)
|
||||
@@ -421,9 +405,9 @@ class TestPOSClosingEntry(ERPNextTestSuite):
|
||||
self.assertEqual(pcv_doc1.grand_total, 300)
|
||||
|
||||
with self.change_settings("POS Settings", {"invoice_type": "Sales Invoice"}):
|
||||
opening_entry2 = create_opening_entry(pos_profile, test_user.name)
|
||||
opening_entry2 = create_opening_entry(self.pos_profile, self.test_user.name)
|
||||
|
||||
pos_si1, pos_si2 = create_multiple_sales_invoices(pos_profile)
|
||||
pos_si1, pos_si2 = create_multiple_sales_invoices(self.pos_profile)
|
||||
|
||||
pos_inv3 = create_pos_invoice(rate=100, do_not_save=1)
|
||||
pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
|
||||
|
||||
@@ -4,6 +4,7 @@ import copy
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_to_date
|
||||
|
||||
from erpnext.accounts.doctype.mode_of_payment.test_mode_of_payment import (
|
||||
set_default_account_for_mode_of_payment,
|
||||
@@ -53,14 +54,14 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
|
||||
w2 = frappe.get_doc(w.doctype, w.name)
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
w.save()
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
frappe.db.set_value(
|
||||
w.doctype,
|
||||
w.name,
|
||||
"modified",
|
||||
add_to_date(w.modified, seconds=1),
|
||||
update_modified=False,
|
||||
)
|
||||
self.assertRaises(frappe.TimestampMismatchError, w2.save)
|
||||
|
||||
def test_change_naming_series(self):
|
||||
@@ -902,9 +903,6 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
self.assertEqual(pos_inv.items[0].rate, 300)
|
||||
|
||||
def test_delivered_serial_no_case(self):
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
|
||||
@@ -916,8 +914,6 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
|
||||
self.assertEqual(serial_no, delivered_serial_no)
|
||||
|
||||
init_user_and_profile()
|
||||
|
||||
pos_inv = create_pos_invoice(
|
||||
item_code="_Test Serialized Item With Series",
|
||||
serial_no=[serial_no],
|
||||
@@ -931,13 +927,9 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
|
||||
def test_bundle_stock_availability_validation(self):
|
||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import ProductBundleStockValidationError
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.test_pos_invoice_merge_log import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
|
||||
init_user_and_profile()
|
||||
from erpnext.stock.utils import get_stock_balance
|
||||
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
@@ -959,9 +951,18 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
is_stock_item=1,
|
||||
)
|
||||
|
||||
# Add initial stock: SubA=5, SubB=2
|
||||
make_stock_entry(item_code=sub_item_a, target=warehouse, qty=5, company=company)
|
||||
make_stock_entry(item_code=sub_item_b, target=warehouse, qty=2, company=company)
|
||||
# Set initial stock to SubA=5 and SubB=2, even when this test is rerun on the same site.
|
||||
for item_code, target_qty in ((sub_item_a, 5), (sub_item_b, 2)):
|
||||
balance = get_stock_balance(item_code, warehouse)
|
||||
difference = target_qty - balance
|
||||
if difference:
|
||||
make_stock_entry(
|
||||
item_code=item_code,
|
||||
to_warehouse=warehouse if difference > 0 else None,
|
||||
from_warehouse=warehouse if difference < 0 else None,
|
||||
qty=abs(difference),
|
||||
company=company,
|
||||
)
|
||||
|
||||
# Create Product Bundle: Test Bundle (SubA x2 + SubB x1)
|
||||
bundle_item = "_Test Bundle"
|
||||
@@ -1010,16 +1011,19 @@ class TestPOSInvoice(POSInvoiceTestMixin):
|
||||
|
||||
def create_pos_invoice(**args):
|
||||
args = frappe._dict(args)
|
||||
pos_profile = None
|
||||
if not args.pos_profile:
|
||||
pos_profile = make_pos_profile()
|
||||
pos_profile.save()
|
||||
pos_profile_name = args.pos_profile
|
||||
if not pos_profile_name:
|
||||
pos_profile_name = frappe.db.exists("POS Profile", "_Test POS Profile")
|
||||
if not pos_profile_name:
|
||||
pos_profile = make_pos_profile()
|
||||
pos_profile.save()
|
||||
pos_profile_name = pos_profile.name
|
||||
|
||||
pos_inv = frappe.new_doc("POS Invoice")
|
||||
pos_inv.update(args)
|
||||
pos_inv.update_stock = 1
|
||||
pos_inv.is_pos = 1
|
||||
pos_inv.pos_profile = args.pos_profile or pos_profile.name
|
||||
pos_inv.pos_profile = pos_profile_name
|
||||
|
||||
if args.posting_date:
|
||||
pos_inv.set_posting_time = 1
|
||||
|
||||
@@ -26,14 +26,10 @@ class TestPOSInvoiceMerging(POSInvoiceTestMixin):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import (
|
||||
make_closing_entry_from_opening,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
|
||||
consolidate_pos_invoices,
|
||||
)
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "amount": 270})
|
||||
pos_inv.save()
|
||||
@@ -55,14 +51,10 @@ class TestPOSInvoiceMerging(POSInvoiceTestMixin):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import (
|
||||
make_closing_entry_from_opening,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
|
||||
consolidate_pos_invoices,
|
||||
)
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "amount": 300})
|
||||
pos_inv.append(
|
||||
@@ -107,9 +99,6 @@ class TestPOSInvoiceMerging(POSInvoiceTestMixin):
|
||||
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import (
|
||||
make_closing_entry_from_opening,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
|
||||
init_user_and_profile,
|
||||
)
|
||||
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
|
||||
consolidate_pos_invoices,
|
||||
)
|
||||
@@ -121,7 +110,6 @@ class TestPOSInvoiceMerging(POSInvoiceTestMixin):
|
||||
make_item(item, {"is_stock_item": 1})
|
||||
make_purchase_receipt(item_code=item, warehouse="_Test Warehouse - _TC", qty=1, rate=300)
|
||||
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1)
|
||||
pos_inv.append("payments", {"mode_of_payment": "Cash", "amount": 300})
|
||||
pos_inv.append(
|
||||
|
||||
@@ -578,17 +578,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
|
||||
make_purchase_invoice as create_purchase_invoice,
|
||||
)
|
||||
|
||||
original_value = frappe.db.get_single_value(
|
||||
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
|
||||
)
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
|
||||
self.addCleanup(
|
||||
frappe.db.set_single_value,
|
||||
"Buying Settings",
|
||||
"set_landed_cost_based_on_purchase_invoice_rate",
|
||||
original_value,
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory",
|
||||
@@ -616,16 +606,7 @@ class TestPurchaseInvoice(ERPNextTestSuite, StockTestMixin):
|
||||
make_purchase_invoice as create_purchase_invoice,
|
||||
)
|
||||
|
||||
original_value = frappe.db.get_single_value(
|
||||
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
|
||||
)
|
||||
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
|
||||
self.addCleanup(
|
||||
frappe.db.set_single_value,
|
||||
"Buying Settings",
|
||||
"set_landed_cost_based_on_purchase_invoice_rate",
|
||||
original_value,
|
||||
)
|
||||
|
||||
pr = frappe.new_doc("Purchase Receipt")
|
||||
pr.currency = "USD"
|
||||
@@ -3545,7 +3526,6 @@ def make_purchase_invoice_against_cost_center(**args):
|
||||
|
||||
def setup_provisional_accounting(**args):
|
||||
args = frappe._dict(args)
|
||||
create_item("_Test Non Stock Item", is_stock_item=0)
|
||||
company = args.company or "_Test Company"
|
||||
provisional_account = create_account(
|
||||
account_name=args.account_name or "Provision Account",
|
||||
|
||||
@@ -7,7 +7,7 @@ import json
|
||||
import frappe
|
||||
from frappe import qb
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from frappe.utils import add_days, cint, flt, format_date, getdate, nowdate, today
|
||||
from frappe.utils import add_days, add_to_date, cint, flt, format_date, getdate, nowdate, today
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
||||
@@ -129,14 +129,14 @@ class TestSalesInvoice(ERPNextTestSuite):
|
||||
|
||||
w2 = frappe.get_doc(w.doctype, w.name)
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
w.save()
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
frappe.db.set_value(
|
||||
w.doctype,
|
||||
w.name,
|
||||
"modified",
|
||||
add_to_date(w.modified, seconds=1),
|
||||
update_modified=False,
|
||||
)
|
||||
self.assertRaises(frappe.TimestampMismatchError, w2.save)
|
||||
|
||||
def test_sales_invoice_change_naming_series(self):
|
||||
@@ -3817,25 +3817,12 @@ class TestSalesInvoice(ERPNextTestSuite):
|
||||
# enable common party accounting
|
||||
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1)
|
||||
|
||||
# create a dimension and make it mandatory
|
||||
if not frappe.get_all("Accounting Dimension", filters={"document_type": "Department"}):
|
||||
dim = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Accounting Dimension",
|
||||
"document_type": "Department",
|
||||
"dimension_defaults": [{"company": "_Test Company", "mandatory_for_bs": True}],
|
||||
}
|
||||
)
|
||||
dim.save()
|
||||
else:
|
||||
dim = frappe.get_doc(
|
||||
"Accounting Dimension",
|
||||
frappe.get_all("Accounting Dimension", filters={"document_type": "Department"})[0],
|
||||
)
|
||||
dim.disabled = False
|
||||
dim.dimension_defaults = []
|
||||
dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True})
|
||||
dim.save()
|
||||
# make the shared department dimension mandatory
|
||||
dim = frappe.get_doc("Accounting Dimension", {"document_type": "Department"})
|
||||
dim.disabled = False
|
||||
dim.dimension_defaults = []
|
||||
dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True})
|
||||
dim.save()
|
||||
|
||||
# create a sales invoice
|
||||
si = create_sales_invoice(
|
||||
@@ -5789,12 +5776,6 @@ def create_internal_parties():
|
||||
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_customer(
|
||||
customer_name="_Test Internal Customer 3",
|
||||
represents_company="_Test Company",
|
||||
@@ -5815,12 +5796,6 @@ def create_internal_parties():
|
||||
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",
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
supplier_name="_Test Internal Customer 3",
|
||||
represents_company="_Test Company",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import frappe
|
||||
from frappe.utils.data import (
|
||||
@@ -658,23 +659,15 @@ class TestSubscription(ERPNextTestSuite):
|
||||
sub2 = create_subscription(start_date="2018-01-02")
|
||||
|
||||
processed = []
|
||||
original_process = Subscription.process
|
||||
original_rollback = frappe.db.rollback
|
||||
|
||||
def patched(self, posting_date=None):
|
||||
processed.append(self.name)
|
||||
if self.name == sub1.name:
|
||||
raise frappe.ValidationError("forced failure")
|
||||
|
||||
Subscription.process = patched
|
||||
# process_all calls frappe.db.rollback() on error which would otherwise wipe
|
||||
# the test transaction; stub it so we can observe the iteration in isolation.
|
||||
frappe.db.rollback = lambda *a, **kw: None
|
||||
try:
|
||||
# Stub transaction recovery so the test can observe the complete iteration in isolation.
|
||||
with patch.object(Subscription, "process", patched), patch.object(frappe.db, "rollback"):
|
||||
process_all([sub1.name, sub2.name])
|
||||
finally:
|
||||
Subscription.process = original_process
|
||||
frappe.db.rollback = original_rollback
|
||||
|
||||
self.assertEqual(processed, [sub1.name, sub2.name])
|
||||
|
||||
@@ -1073,12 +1066,6 @@ def create_plan(**kwargs):
|
||||
|
||||
|
||||
def create_parties():
|
||||
if not frappe.db.exists("Supplier", "_Test Supplier"):
|
||||
supplier = frappe.new_doc("Supplier")
|
||||
supplier.supplier_name = "_Test Supplier"
|
||||
supplier.supplier_group = "All Supplier Groups"
|
||||
supplier.insert()
|
||||
|
||||
if not frappe.db.exists("Customer", "_Test Subscription Customer"):
|
||||
customer = frappe.new_doc("Customer")
|
||||
customer.customer_name = "_Test Subscription Customer"
|
||||
|
||||
@@ -63,25 +63,6 @@ class TestTaxRule(ERPNextTestSuite):
|
||||
|
||||
def test_for_parent_supplier_group(self):
|
||||
purchase_template = "_Test Purchase Taxes and Charges Template - _TC"
|
||||
if not frappe.db.exists("Purchase Taxes and Charges Template", purchase_template):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges Template",
|
||||
"title": "_Test Purchase Taxes and Charges Template",
|
||||
"company": "_Test Company",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"charge_type": "On Net Total",
|
||||
"description": "VAT",
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"cost_center": "Main - _TC",
|
||||
"rate": 6,
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert()
|
||||
|
||||
make_tax_rule(
|
||||
supplier_group="All Supplier Groups",
|
||||
tax_type="Purchase",
|
||||
|
||||
@@ -869,9 +869,7 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin):
|
||||
self.assertEqual(rows_b[0].future_amount, 50.0)
|
||||
|
||||
def test_sales_person(self):
|
||||
sales_person = frappe.get_doc(
|
||||
{"doctype": "Sales Person", "sales_person_name": "John Clark", "enabled": True}
|
||||
).insert()
|
||||
sales_person = frappe.get_doc("Sales Person", "_Test Sales Person")
|
||||
si = self.create_sales_invoice(do_not_submit=True)
|
||||
si.append("sales_team", {"sales_person": sales_person.name, "allocated_percentage": 100})
|
||||
si.save().submit()
|
||||
@@ -1494,17 +1492,8 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin):
|
||||
|
||||
def test_receivable_filtered_by_sales_partner(self):
|
||||
frappe.set_user("Administrator")
|
||||
partner_a, partner_b = "_Test AR Sales Partner A", "_Test AR Sales Partner B"
|
||||
for partner in (partner_a, partner_b):
|
||||
if not frappe.db.exists("Sales Partner", partner):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Sales Partner",
|
||||
"partner_name": partner,
|
||||
"commission_rate": 0,
|
||||
"territory": "All Territories",
|
||||
}
|
||||
).insert()
|
||||
partner_a = "_Test Sales Partner India - 1"
|
||||
partner_b = "_Test Sales Partner India - 2"
|
||||
|
||||
def _si(sales_partner):
|
||||
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True, qty=2)
|
||||
|
||||
@@ -193,16 +193,7 @@ class TestAccountsReceivable(ERPNextTestSuite, AccountsTestMixin):
|
||||
self.assertEqual(len(rpt_output), 0)
|
||||
|
||||
def test_03_summary_sales_partner_column(self):
|
||||
partner = "_Test AR Summary Sales Partner"
|
||||
if not frappe.db.exists("Sales Partner", partner):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Sales Partner",
|
||||
"partner_name": partner,
|
||||
"commission_rate": 0,
|
||||
"territory": "All Territories",
|
||||
}
|
||||
).insert()
|
||||
partner = "_Test Sales Partner India - 1"
|
||||
|
||||
si = create_sales_invoice(
|
||||
item=self.item,
|
||||
|
||||
@@ -21,10 +21,8 @@ class TestGeneralLedger(ERPNextTestSuite):
|
||||
from frappe.utils import today
|
||||
|
||||
frappe.db.set_single_value("Accounts Settings", "general_ledger_remarks_length", 50)
|
||||
self.addCleanup(frappe.db.set_single_value, "Accounts Settings", "general_ledger_remarks_length", 0)
|
||||
|
||||
si = create_sales_invoice(company=self.company)
|
||||
self.addCleanup(self._cancel_and_delete, "Sales Invoice", si.name)
|
||||
create_sales_invoice(company=self.company)
|
||||
|
||||
columns, data = execute(
|
||||
frappe._dict(
|
||||
@@ -42,15 +40,6 @@ class TestGeneralLedger(ERPNextTestSuite):
|
||||
self.assertTrue(data)
|
||||
self.assertTrue(any("remarks" in row for row in data))
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
def clear_old_entries(self):
|
||||
doctype_list = [
|
||||
"GL Entry",
|
||||
|
||||
@@ -642,7 +642,7 @@ class TestGrossProfit(ERPNextTestSuite):
|
||||
self.assertEqual(total.get("gross_profit_%"), -50.0)
|
||||
|
||||
def test_sales_person_wise_gross_profit(self):
|
||||
sales_person = make_sales_person("_Test Sales Person")
|
||||
sales_person = frappe.get_doc("Sales Person", "_Test Sales Person")
|
||||
|
||||
posting_date = get_first_day(nowdate())
|
||||
qty = 10
|
||||
@@ -1194,19 +1194,3 @@ class TestGrossProfit(ERPNextTestSuite):
|
||||
self.assertEqual(base_rate, 220.0) # avg selling rate = 220/1
|
||||
self.assertEqual(gross_profit, 120.0) # 220 - 100
|
||||
self.assertAlmostEqual(gp_percent, 54.545, places=2) # 120/220 * 100
|
||||
|
||||
|
||||
def make_sales_person(sales_person_name="_Test Sales Person"):
|
||||
if not frappe.db.exists("Sales Person", {"sales_person_name": sales_person_name}):
|
||||
sales_person_doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Sales Person",
|
||||
"is_group": 0,
|
||||
"parent_sales_person": "Sales Team",
|
||||
"sales_person_name": sales_person_name,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
else:
|
||||
sales_person_doc = frappe.get_doc("Sales Person", {"sales_person_name": sales_person_name})
|
||||
|
||||
return sales_person_doc
|
||||
|
||||
@@ -12,7 +12,7 @@ COMPANY = "_Test Company"
|
||||
class TestShareBalanceReport(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.share_type = create_share_type("_Test Share Balance Equity")
|
||||
self.shareholder = create_shareholder("_Test Share Balance Holder", COMPANY)
|
||||
self.shareholder = get_shareholder("Iron Man", COMPANY)
|
||||
|
||||
def test_date_filter_is_mandatory(self):
|
||||
self.assertRaises(frappe.ValidationError, execute, frappe._dict({"shareholder": self.shareholder}))
|
||||
@@ -96,7 +96,7 @@ class TestShareBalanceReport(ERPNextTestSuite):
|
||||
self.assertEqual(row[4], 3000)
|
||||
|
||||
def test_balance_reduces_after_transfer_out(self):
|
||||
other_holder = create_shareholder("_Test Share Balance Holder 2", COMPANY)
|
||||
other_holder = get_shareholder("Thor", COMPANY)
|
||||
create_share_transfer(
|
||||
transfer_type="Issue",
|
||||
to_shareholder=self.shareholder,
|
||||
@@ -187,9 +187,8 @@ def create_share_type(title):
|
||||
return title
|
||||
|
||||
|
||||
def create_shareholder(title, company):
|
||||
shareholder = frappe.get_doc({"doctype": "Shareholder", "title": title, "company": company}).insert()
|
||||
return shareholder.name
|
||||
def get_shareholder(title, company):
|
||||
return frappe.db.get_value("Shareholder", {"title": title, "company": company}, "name")
|
||||
|
||||
|
||||
def create_share_transfer(**kwargs):
|
||||
|
||||
@@ -23,7 +23,7 @@ COL_SHARE_TRANSFER = 8
|
||||
|
||||
class TestShareLedger(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.shareholder = self.create_shareholder("_Test Share Ledger Holder")
|
||||
self.shareholder = self.get_shareholder("Iron Man")
|
||||
# Issue 100 shares on 2026-06-01, then another 50 on 2026-06-10.
|
||||
self.first = self.issue_shares(date="2026-06-01", from_no=1, to_no=100, rate=10)
|
||||
self.second = self.issue_shares(date="2026-06-10", from_no=101, to_no=150, rate=12)
|
||||
@@ -72,7 +72,7 @@ class TestShareLedger(ERPNextTestSuite):
|
||||
self.assertEqual(data[0][COL_NO_OF_SHARES], 100)
|
||||
|
||||
def test_transfer_type_label_when_shareholder_is_seller(self):
|
||||
buyer = self.create_shareholder("_Test Share Ledger Buyer")
|
||||
buyer = self.get_shareholder("Thor")
|
||||
transfer = self.make_transfer(
|
||||
from_shareholder=self.shareholder,
|
||||
to_shareholder=buyer,
|
||||
@@ -87,7 +87,7 @@ class TestShareLedger(ERPNextTestSuite):
|
||||
self.assertEqual(row[COL_TRANSFER_TYPE], f"Transfer to {buyer}")
|
||||
|
||||
def test_transfer_type_label_when_shareholder_is_buyer(self):
|
||||
seller = self.create_shareholder("_Test Share Ledger Seller")
|
||||
seller = self.get_shareholder("Hulk")
|
||||
# the seller must own shares before it can transfer them
|
||||
self.issue_shares(date="2026-06-12", from_no=201, to_no=300, rate=10, shareholder=seller)
|
||||
transfer = self.make_transfer(
|
||||
@@ -119,15 +119,8 @@ class TestShareLedger(ERPNextTestSuite):
|
||||
self.assertIsNotNone(row, f"Share Transfer {transfer_name} missing from ledger")
|
||||
return row
|
||||
|
||||
def create_shareholder(self, title):
|
||||
doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Shareholder",
|
||||
"title": title,
|
||||
"company": COMPANY,
|
||||
}
|
||||
).insert()
|
||||
return doc.name
|
||||
def get_shareholder(self, title):
|
||||
return frappe.db.get_value("Shareholder", {"title": title, "company": COMPANY}, "name")
|
||||
|
||||
def issue_shares(self, date, from_no, to_no, rate, shareholder=None):
|
||||
doc = frappe.get_doc(
|
||||
|
||||
@@ -2109,13 +2109,17 @@ def create_asset_category(enable_cwip=1):
|
||||
|
||||
|
||||
def create_fixed_asset_item(item_code=None, auto_create_assets=1, is_grouped_asset=0, asset_category=None):
|
||||
item_code = item_code or "Macbook Pro"
|
||||
if frappe.db.exists("Item", item_code):
|
||||
return frappe.get_doc("Item", item_code)
|
||||
|
||||
meta = frappe.get_meta("Asset")
|
||||
naming_series = meta.get_field("naming_series").options.splitlines()[0] or "ACC-ASS-.YYYY.-"
|
||||
try:
|
||||
item = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Item",
|
||||
"item_code": item_code or "Macbook Pro",
|
||||
"item_code": item_code,
|
||||
"item_name": "Macbook Pro",
|
||||
"description": "Macbook Pro Retina Display",
|
||||
"asset_category": asset_category or "Computers",
|
||||
|
||||
@@ -54,14 +54,16 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
|
||||
for item_row in doc.stock_items:
|
||||
sle_list = self.sle_map.get(item_row.name)
|
||||
if sle_list:
|
||||
_inv_dict = doc.get_inventory_account_dict(item_row, self.inventory_account_map)
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, self.precision)
|
||||
|
||||
if erpnext.is_perpetual_inventory_enabled(doc.company):
|
||||
_inv_dict = doc.get_inventory_account_dict(item_row, self.inventory_account_map)
|
||||
account = _inv_dict["account"]
|
||||
account_currency = _inv_dict["account_currency"]
|
||||
else:
|
||||
account = doc.get_company_default("default_expense_account")
|
||||
account_currency = None
|
||||
|
||||
target_against.add(account)
|
||||
gl_entries.append(
|
||||
@@ -74,7 +76,7 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
|
||||
"remarks": doc.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": -1 * stock_value_difference,
|
||||
},
|
||||
_inv_dict["account_currency"],
|
||||
account_currency,
|
||||
item=item_row,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -440,7 +440,11 @@ def create_asset_capitalization(**args):
|
||||
target_asset = frappe.get_doc("Asset", args.target_asset) if args.target_asset else frappe._dict()
|
||||
target_item_code = target_asset.item_code or args.target_item_code
|
||||
company = target_asset.company or args.company or "_Test Company"
|
||||
warehouse = args.warehouse or create_warehouse("_Test Warehouse", company=company)
|
||||
warehouse = args.warehouse or (
|
||||
"_Test Warehouse - _TC"
|
||||
if company == "_Test Company"
|
||||
else create_warehouse("_Test Warehouse", company=company)
|
||||
)
|
||||
source_warehouse = args.source_warehouse or warehouse
|
||||
|
||||
asset_capitalization = frappe.new_doc("Asset Capitalization")
|
||||
|
||||
@@ -20,7 +20,6 @@ from erpnext.assets.doctype.asset.test_asset import (
|
||||
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
|
||||
get_asset_depr_schedule_doc,
|
||||
)
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
|
||||
get_serial_nos_from_bundle,
|
||||
make_serial_batch_bundle,
|
||||
@@ -32,7 +31,6 @@ class TestAssetRepair(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.load_test_records("Stock Entry")
|
||||
set_depreciation_settings_in_company()
|
||||
create_item("_Test Stock Item")
|
||||
|
||||
def test_asset_status(self):
|
||||
date = nowdate()
|
||||
|
||||
@@ -378,9 +378,7 @@ class TestPurchaseOrder(ERPNextTestSuite):
|
||||
po.submit()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
|
||||
company_default = frappe.db.get_value("Company", po.company, "default_warehouse")
|
||||
frappe.db.set_value("Company", po.company, "default_warehouse", None)
|
||||
self.addCleanup(frappe.db.set_value, "Company", po.company, "default_warehouse", company_default)
|
||||
|
||||
def get_trans_items(item_code):
|
||||
return json.dumps(
|
||||
@@ -794,14 +792,9 @@ class TestPurchaseOrder(ERPNextTestSuite):
|
||||
self.assertRaises(frappe.ValidationError, below_minimum.insert)
|
||||
|
||||
def test_marginal_min_order_qty_overage_toast(self):
|
||||
original_precision = frappe.db.get_default("float_precision")
|
||||
frappe.db.set_default("float_precision", "3")
|
||||
self.addCleanup(frappe.db.set_default, "float_precision", original_precision)
|
||||
|
||||
if not frappe.db.exists("UOM", "Gram"):
|
||||
frappe.get_doc({"doctype": "UOM", "uom_name": "Gram"}).insert()
|
||||
|
||||
item_doc = make_item(properties={"min_order_qty": 50000, "stock_uom": "Gram"})
|
||||
item_doc = make_item(properties={"min_order_qty": 50000, "stock_uom": "_Test UOM 1"})
|
||||
item_doc.append("uoms", {"uom": "Pound", "conversion_factor": 453.592292197})
|
||||
item_doc.save()
|
||||
item = item_doc.name
|
||||
@@ -1799,25 +1792,11 @@ def create_po_for_sc_testing():
|
||||
|
||||
|
||||
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"
|
||||
|
||||
create_internal_customer(
|
||||
"_Test Internal Customer 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
"_Test Internal Supplier 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
|
||||
warehouse = create_warehouse("_Test Internal Warehouse New 1", company=company)
|
||||
|
||||
create_warehouse("_Test Internal Warehouse GIT", company=company)
|
||||
|
||||
@@ -15,6 +15,7 @@ from erpnext.buying.doctype.request_for_quotation.test_request_for_quotation imp
|
||||
from erpnext.buying.doctype.supplier_quotation.mapper import make_purchase_order
|
||||
from erpnext.buying.doctype.supplier_quotation.supplier_quotation import set_expired_status
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError, update_child_qty_rate
|
||||
from erpnext.tests.assertions import assert_raises_with_savepoint
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -161,12 +162,9 @@ class TestPurchaseOrder(ERPNextTestSuite):
|
||||
]
|
||||
)
|
||||
|
||||
frappe.db.savepoint("before_cancel")
|
||||
# check if item having purchase order can be removed
|
||||
self.assertRaises(
|
||||
frappe.LinkExistsError, update_child_qty_rate, "Supplier Quotation", trans_item, sq.name
|
||||
)
|
||||
frappe.db.rollback(save_point="before_cancel")
|
||||
with assert_raises_with_savepoint(self, frappe.LinkExistsError):
|
||||
update_child_qty_rate("Supplier Quotation", trans_item, sq.name)
|
||||
|
||||
trans_item = json.dumps(
|
||||
[
|
||||
|
||||
@@ -53,7 +53,6 @@ class TestAccountsController(ERPNextTestSuite):
|
||||
self.item = "_Test Item"
|
||||
self.customer = "_Test Customer USD"
|
||||
self.supplier = "_Test Supplier USD"
|
||||
self.create_account()
|
||||
frappe.flags.is_reverse_depr_entry = False
|
||||
|
||||
def create_account(self):
|
||||
@@ -99,6 +98,7 @@ class TestAccountsController(ERPNextTestSuite):
|
||||
setattr(self, x.attribute_name, acc.name)
|
||||
|
||||
def setup_advance_accounts_in_party_master(self):
|
||||
self.create_account()
|
||||
company = frappe.get_doc("Company", self.company)
|
||||
company.book_advance_payments_in_separate_party_account = 1
|
||||
company.save()
|
||||
|
||||
@@ -7,15 +7,6 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
def test_sales_return_validates_against_original(self):
|
||||
# Submitting a return Delivery Note runs validate_returned_items (Item / Packed Item lookups
|
||||
# via frappe.get_all) and get_already_returned_items (qb GROUP BY of the returned qty) -- both
|
||||
@@ -24,16 +15,13 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
se = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
|
||||
dn = create_delivery_note(qty=5)
|
||||
self.addCleanup(self._cancel_and_delete, "Delivery Note", dn.name)
|
||||
|
||||
return_dn = make_sales_return(dn.name)
|
||||
return_dn.insert()
|
||||
return_dn.submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Delivery Note", return_dn.name)
|
||||
|
||||
self.assertEqual(return_dn.is_return, 1)
|
||||
self.assertEqual(return_dn.items[0].qty, -5)
|
||||
@@ -44,7 +32,6 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
|
||||
pi = make_purchase_invoice(qty=10)
|
||||
self.addCleanup(self._cancel_and_delete, "Purchase Invoice", pi.name)
|
||||
|
||||
return_pi = make_purchase_invoice(
|
||||
is_return=1,
|
||||
@@ -66,7 +53,6 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
pi.items[0].item_code = ""
|
||||
pi.save()
|
||||
pi.submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Purchase Invoice", pi.name)
|
||||
|
||||
return_pi = make_purchase_invoice(
|
||||
item_name="_Test Item",
|
||||
@@ -86,11 +72,9 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
se = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
|
||||
dn = create_delivery_note(qty=5)
|
||||
self.addCleanup(self._cancel_and_delete, "Delivery Note", dn.name)
|
||||
|
||||
return_dn = make_sales_return(dn.name)
|
||||
return_dn.items[0].qty = 0
|
||||
@@ -104,7 +88,6 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
|
||||
si = create_sales_invoice(qty=10)
|
||||
self.addCleanup(self._cancel_and_delete, "Sales Invoice", si.name)
|
||||
|
||||
return_si = make_return_doc(si.doctype, si.name)
|
||||
return_si.items[0].qty = 0
|
||||
@@ -131,14 +114,11 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite):
|
||||
si.items[0].stock_uom = "Kg"
|
||||
si.items[0].conversion_factor = 0.013888889
|
||||
si.save().submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Sales Invoice", si.name)
|
||||
|
||||
first_return = make_return_doc(si.doctype, si.name)
|
||||
first_return.items[0].qty = -24
|
||||
first_return.save().submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Sales Invoice", first_return.name)
|
||||
|
||||
second_return = make_return_doc(si.doctype, si.name)
|
||||
self.assertEqual(second_return.items[0].qty, -24)
|
||||
second_return.save().submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Sales Invoice", second_return.name)
|
||||
|
||||
@@ -7,15 +7,6 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestSellingControllerConversions(ERPNextTestSuite):
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
def test_partial_delivery_updates_sales_order_status(self):
|
||||
# Submitting a Delivery Note against a Sales Order calls
|
||||
# SellingController.get_already_delivered_qty / get_so_qty_and_warehouse and StatusUpdater
|
||||
@@ -24,8 +15,7 @@ class TestSellingControllerConversions(ERPNextTestSuite):
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
se = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100)
|
||||
|
||||
so = make_sales_order(qty=10)
|
||||
|
||||
@@ -33,7 +23,6 @@ class TestSellingControllerConversions(ERPNextTestSuite):
|
||||
dn.items[0].qty = 4
|
||||
dn.insert()
|
||||
dn.submit()
|
||||
self.addCleanup(self._cancel_and_delete, "Delivery Note", dn.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.per_delivered, 40.0)
|
||||
|
||||
@@ -8,15 +8,6 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestStockControllerConversions(ERPNextTestSuite):
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
def test_future_sle_exists_detects_later_entries(self):
|
||||
# future_sle_exists / get_conditions_to_validate_future_sle were converted to query builder
|
||||
# (Count + Criterion.any). A later SLE for the same item+warehouse must be detected, which
|
||||
@@ -26,8 +17,7 @@ class TestStockControllerConversions(ERPNextTestSuite):
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
item = make_item("_Test Future SLE Item", {"is_stock_item": 1}).name
|
||||
se = make_stock_entry(item_code=item, target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
make_stock_entry(item_code=item, target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||
|
||||
# Pretend a different voucher posts a day earlier for the same item/warehouse: the existing
|
||||
# (later) SLE must be reported as a future entry.
|
||||
@@ -52,7 +42,6 @@ class TestStockControllerConversions(ERPNextTestSuite):
|
||||
posting_date=add_days(today(), -5),
|
||||
posting_time="01:00:00",
|
||||
)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", opening.name)
|
||||
|
||||
return opening
|
||||
|
||||
@@ -106,7 +95,6 @@ class TestStockControllerConversions(ERPNextTestSuite):
|
||||
finally:
|
||||
stock_ledger.make_entry = original_make_entry
|
||||
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", entry.name)
|
||||
if inject is not None:
|
||||
self.assertTrue(injected, "the later SL Entry was not written during the submit")
|
||||
|
||||
@@ -126,9 +114,6 @@ class TestStockControllerConversions(ERPNextTestSuite):
|
||||
pluck="name",
|
||||
)
|
||||
)
|
||||
for name in names:
|
||||
self.addCleanup(frappe.delete_doc, "Repost Item Valuation", name, force=1)
|
||||
|
||||
return names
|
||||
|
||||
def test_repost_queued_for_entry_backdated_while_its_sl_entries_were_written(self):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
from contextlib import contextmanager
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import frappe
|
||||
@@ -51,12 +52,9 @@ SAMPLE_GENERICODE = b"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
class TestCodeListImport(ERPNextTestSuite):
|
||||
def test_import_genericode_rejects_remote_file_url(self):
|
||||
self.set_upload_context(
|
||||
file_name="trusted.xml",
|
||||
file_url="https://example.com/codelists/trusted.xml",
|
||||
)
|
||||
|
||||
with patch("erpnext.edi.doctype.code_list.code_list_import.requests.get") as mock_get:
|
||||
with self.upload_context(
|
||||
file_name="trusted.xml", file_url="https://example.com/codelists/trusted.xml"
|
||||
), patch("erpnext.edi.doctype.code_list.code_list_import.requests.get") as mock_get:
|
||||
with self.assertRaisesRegex(
|
||||
frappe.ValidationError, "Importing Code Lists from remote URLs is not allowed."
|
||||
):
|
||||
@@ -65,12 +63,9 @@ class TestCodeListImport(ERPNextTestSuite):
|
||||
mock_get.assert_not_called()
|
||||
|
||||
def test_import_genericode_rejects_file_scheme_url(self):
|
||||
self.set_upload_context(
|
||||
file_name="trusted.xml",
|
||||
file_url="file:///tmp/trusted.xml",
|
||||
)
|
||||
|
||||
with patch("erpnext.edi.doctype.code_list.code_list_import.requests.get") as mock_get:
|
||||
with self.upload_context(file_name="trusted.xml", file_url="file:///tmp/trusted.xml"), patch(
|
||||
"erpnext.edi.doctype.code_list.code_list_import.requests.get"
|
||||
) as mock_get:
|
||||
with self.assertRaisesRegex(
|
||||
frappe.ValidationError, "Importing Code Lists from remote URLs is not allowed."
|
||||
):
|
||||
@@ -110,36 +105,34 @@ class TestCodeListImport(ERPNextTestSuite):
|
||||
code_list_import.import_genericode_from_url("https://example.com/codelists/trusted.xml")
|
||||
|
||||
def test_import_genericode_from_uploaded_file_returns_metadata(self):
|
||||
self.set_upload_context(content=SAMPLE_GENERICODE, file_name="uploaded_genericode.xml")
|
||||
with self.upload_context(content=SAMPLE_GENERICODE, file_name="uploaded_genericode.xml"):
|
||||
import_result = code_list_import.import_genericode()
|
||||
|
||||
import_result = code_list_import.import_genericode()
|
||||
self.assert_import_response(import_result)
|
||||
|
||||
self.assert_import_response(import_result)
|
||||
|
||||
file_doc = frappe.get_doc("File", import_result["file"])
|
||||
self.assertEqual(file_doc.get_content(encodings=()), SAMPLE_GENERICODE)
|
||||
file_doc = frappe.get_doc("File", import_result["file"])
|
||||
self.assertEqual(file_doc.get_content(encodings=()), SAMPLE_GENERICODE)
|
||||
|
||||
def test_process_genericode_import_reads_file_doc_content(self):
|
||||
self.set_upload_context(content=SAMPLE_GENERICODE, file_name="uploaded_genericode.xml")
|
||||
with self.upload_context(content=SAMPLE_GENERICODE, file_name="uploaded_genericode.xml"):
|
||||
import_result = code_list_import.import_genericode()
|
||||
count = code_list_import.process_genericode_import(
|
||||
code_list_name=import_result["code_list"],
|
||||
file_name=import_result["file"],
|
||||
code_column="code",
|
||||
title_column="name",
|
||||
)
|
||||
|
||||
import_result = code_list_import.import_genericode()
|
||||
count = code_list_import.process_genericode_import(
|
||||
code_list_name=import_result["code_list"],
|
||||
file_name=import_result["file"],
|
||||
code_column="code",
|
||||
title_column="name",
|
||||
)
|
||||
|
||||
self.assertEqual(count, 3)
|
||||
self.assertEqual(frappe.db.count("Common Code", {"code_list": import_result["code_list"]}), 3)
|
||||
self.assertEqual(
|
||||
frappe.db.get_value(
|
||||
"Common Code",
|
||||
{"code_list": import_result["code_list"], "common_code": "A"},
|
||||
"title",
|
||||
),
|
||||
"Alpha",
|
||||
)
|
||||
self.assertEqual(count, 3)
|
||||
self.assertEqual(frappe.db.count("Common Code", {"code_list": import_result["code_list"]}), 3)
|
||||
self.assertEqual(
|
||||
frappe.db.get_value(
|
||||
"Common Code",
|
||||
{"code_list": import_result["code_list"], "common_code": "A"},
|
||||
"title",
|
||||
),
|
||||
"Alpha",
|
||||
)
|
||||
|
||||
def test_import_genericode_from_local_file_url(self):
|
||||
source_file = frappe.get_doc(
|
||||
@@ -150,32 +143,38 @@ class TestCodeListImport(ERPNextTestSuite):
|
||||
"is_private": 1,
|
||||
}
|
||||
).insert()
|
||||
self.set_upload_context(file_name=source_file.file_name, file_url=source_file.file_url)
|
||||
with self.upload_context(file_name=source_file.file_name, file_url=source_file.file_url):
|
||||
import_result = code_list_import.import_genericode()
|
||||
|
||||
import_result = code_list_import.import_genericode()
|
||||
self.assert_import_response(import_result)
|
||||
|
||||
self.assert_import_response(import_result)
|
||||
|
||||
def set_upload_context(
|
||||
self,
|
||||
@staticmethod
|
||||
@contextmanager
|
||||
def upload_context(
|
||||
content: bytes | None = None,
|
||||
file_name: str = "genericode.xml",
|
||||
file_url: str | None = None,
|
||||
docname: str | None = None,
|
||||
):
|
||||
attrs = ("form_dict", "uploaded_file", "uploaded_file_url", "uploaded_filename")
|
||||
originals = {attr: getattr(frappe.local, attr, None) for attr in attrs}
|
||||
missing = object()
|
||||
attrs = {
|
||||
"form_dict": frappe._dict(doctype="Code List", docname=docname),
|
||||
"uploaded_file": content,
|
||||
"uploaded_file_url": file_url,
|
||||
"uploaded_filename": file_name,
|
||||
}
|
||||
originals = {key: getattr(frappe.local, key, missing) for key in attrs}
|
||||
for key, value in attrs.items():
|
||||
setattr(frappe.local, key, value)
|
||||
|
||||
frappe.local.form_dict = frappe._dict(doctype="Code List", docname=docname)
|
||||
frappe.local.uploaded_file = content
|
||||
frappe.local.uploaded_file_url = file_url
|
||||
frappe.local.uploaded_filename = file_name
|
||||
|
||||
def restore():
|
||||
for attr, value in originals.items():
|
||||
setattr(frappe.local, attr, value)
|
||||
|
||||
self.addCleanup(restore)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
for key, value in originals.items():
|
||||
if value is missing:
|
||||
delattr(frappe.local, key)
|
||||
else:
|
||||
setattr(frappe.local, key, value)
|
||||
|
||||
def assert_import_response(self, import_result):
|
||||
self.assertEqual(
|
||||
|
||||
@@ -70,6 +70,8 @@ after_install = "erpnext.setup.install.after_install"
|
||||
after_app_install = "erpnext.setup.install.after_app_install"
|
||||
after_app_uninstall = "erpnext.setup.install.after_app_uninstall"
|
||||
|
||||
before_tests = "erpnext.tests.utils.bootstrap_test_data"
|
||||
|
||||
boot_session = "erpnext.startup.boot.boot_session"
|
||||
notification_config = "erpnext.startup.notifications.get_notification_config"
|
||||
get_help_messages = "erpnext.utilities.activation.get_help_messages"
|
||||
|
||||
@@ -18,25 +18,6 @@ class TestMaintenanceSchedule(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.load_test_records("Stock Entry")
|
||||
|
||||
@classmethod
|
||||
def make_sales_person(cls):
|
||||
records = [
|
||||
{
|
||||
"doctype": "Sales Person",
|
||||
"is_group": 0,
|
||||
"parent_sales_person": "Sales Team",
|
||||
"sales_person_name": "_Test Sales Person",
|
||||
},
|
||||
]
|
||||
cls.sales_person = []
|
||||
for x in records:
|
||||
if not frappe.db.exists("Sales Person", {"sales_person_name": x.get("sales_person_name")}):
|
||||
cls.sales_person.append(frappe.get_doc(x).insert())
|
||||
else:
|
||||
cls.sales_person.append(
|
||||
frappe.get_doc("Sales Person", {"sales_person_name": x.get("sales_person_name")})
|
||||
)
|
||||
|
||||
def test_events_should_be_created_and_deleted(self):
|
||||
ms = make_maintenance_schedule()
|
||||
ms.generate_schedule()
|
||||
|
||||
@@ -9,7 +9,7 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
class TestMaintenanceVisit(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.sales_person = make_sales_person("_Test Maintenance Service Person")
|
||||
self.sales_person = frappe.get_doc("Sales Person", "_Test Sales Person")
|
||||
|
||||
def make_warranty_claim(self):
|
||||
# Warranty Claim is not submittable; it provides a real target for the
|
||||
@@ -129,14 +129,6 @@ class TestMaintenanceVisit(ERPNextTestSuite):
|
||||
self.assertIsNone(claim.resolution_date)
|
||||
|
||||
|
||||
def make_sales_person(name):
|
||||
sales_person = frappe.get_doc({"doctype": "Sales Person", "sales_person_name": name})
|
||||
sales_person.insert(ignore_if_duplicate=True)
|
||||
if not sales_person.name:
|
||||
sales_person = frappe.get_doc("Sales Person", {"sales_person_name": name})
|
||||
return sales_person
|
||||
|
||||
|
||||
def make_maintenance_visit():
|
||||
mv = frappe.new_doc("Maintenance Visit")
|
||||
mv.company = "_Test Company"
|
||||
@@ -144,8 +136,6 @@ def make_maintenance_visit():
|
||||
mv.mntc_date = today()
|
||||
mv.completion_status = "Partially Completed"
|
||||
|
||||
sales_person = make_sales_person("Dwight Schrute")
|
||||
|
||||
mv.append(
|
||||
"purposes",
|
||||
{
|
||||
@@ -153,7 +143,7 @@ def make_maintenance_visit():
|
||||
"sales_person": "Sales Team",
|
||||
"description": "Test Item",
|
||||
"work_done": "Test Work Done",
|
||||
"service_person": sales_person.name,
|
||||
"service_person": "_Test Sales Person",
|
||||
},
|
||||
)
|
||||
mv.insert(ignore_permissions=True)
|
||||
|
||||
@@ -2389,9 +2389,7 @@ class TestProductionPlan(ERPNextTestSuite):
|
||||
_quantity_in_purchase_uom,
|
||||
)
|
||||
|
||||
original_precision = frappe.db.get_default("float_precision")
|
||||
frappe.db.set_default("float_precision", "3")
|
||||
self.addCleanup(frappe.db.set_default, "float_precision", original_precision)
|
||||
|
||||
self.assertEqual(_quantity_in_purchase_uom(50000, 453.592292197, 50000), 110.232)
|
||||
self.assertEqual(_quantity_in_purchase_uom(2000, 0.453592, 2000), 4409.249)
|
||||
@@ -2399,9 +2397,7 @@ class TestProductionPlan(ERPNextTestSuite):
|
||||
self.assertEqual(_quantity_in_purchase_uom(50000, 453.592292197), 110.231)
|
||||
|
||||
def test_min_order_qty_grid_ceiling_in_plan_items(self):
|
||||
original_precision = frappe.db.get_default("float_precision")
|
||||
frappe.db.set_default("float_precision", "3")
|
||||
self.addCleanup(frappe.db.set_default, "float_precision", original_precision)
|
||||
|
||||
conversion_factor = 453.592292197
|
||||
fg_item = make_item(properties={"is_stock_item": 1}).name
|
||||
@@ -2422,9 +2418,7 @@ class TestProductionPlan(ERPNextTestSuite):
|
||||
def test_min_order_qty_grid_ceiling_from_other_locations(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
original_precision = frappe.db.get_default("float_precision")
|
||||
frappe.db.set_default("float_precision", "3")
|
||||
self.addCleanup(frappe.db.set_default, "float_precision", original_precision)
|
||||
|
||||
conversion_factor = 453.592292197
|
||||
fg_item = make_item(properties={"is_stock_item": 1}).name
|
||||
|
||||
@@ -85,6 +85,33 @@ class TestRouting(ERPNextTestSuite):
|
||||
self.assertEqual(bom_doc.operations[0].time_in_mins, 30)
|
||||
self.assertEqual(bom_doc.operations[1].time_in_mins, 20)
|
||||
|
||||
def test_create_routing_isolates_operation_lists(self):
|
||||
first = create_routing(
|
||||
routing_name="Testing Route Isolation",
|
||||
operations=[
|
||||
{
|
||||
"operation": "_Test Operation 1",
|
||||
"workstation": "_Test Workstation 1",
|
||||
"time_in_mins": 10,
|
||||
}
|
||||
],
|
||||
)
|
||||
second = create_routing(
|
||||
routing_name="Testing Route Isolation",
|
||||
operations=[
|
||||
{
|
||||
"operation": "_Test Operation 1",
|
||||
"workstation": "_Test Workstation 1",
|
||||
"time_in_mins": 20,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
first.reload()
|
||||
self.assertNotEqual(first.name, second.name)
|
||||
self.assertEqual(first.operations[0].time_in_mins, 10)
|
||||
self.assertEqual(second.operations[0].time_in_mins, 20)
|
||||
|
||||
|
||||
def setup_operations(rows):
|
||||
from erpnext.manufacturing.doctype.operation.test_operation import make_operation
|
||||
@@ -102,17 +129,8 @@ def create_routing(**args):
|
||||
doc.update(args)
|
||||
|
||||
if not args.do_not_save:
|
||||
frappe.db.savepoint("create_routing")
|
||||
try:
|
||||
doc.insert()
|
||||
except frappe.DuplicateEntryError:
|
||||
frappe.db.rollback(save_point="create_routing")
|
||||
doc = frappe.get_doc("Routing", args.routing_name)
|
||||
doc.delete_key("operations")
|
||||
for operation in args.operations:
|
||||
doc.append("operations", operation)
|
||||
|
||||
doc.save()
|
||||
doc.routing_name = f"{args.routing_name}-{frappe.generate_hash(length=10)}"
|
||||
doc.insert()
|
||||
|
||||
return doc
|
||||
|
||||
@@ -136,7 +154,11 @@ def setup_bom(**args):
|
||||
|
||||
args.raw_materials = ["Test Extra Item N-1"]
|
||||
|
||||
name = frappe.db.get_value("BOM", {"item": args.item_code}, "name")
|
||||
name = frappe.db.get_value(
|
||||
"BOM",
|
||||
{"item": args.item_code, "routing": args.routing, "docstatus": 1},
|
||||
"name",
|
||||
)
|
||||
if not name:
|
||||
bom_doc = make_bom(
|
||||
item=args.item_code,
|
||||
|
||||
@@ -44,7 +44,6 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.warehouse = "_Test Warehouse 2 - _TC"
|
||||
self.item = "_Test Item"
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
|
||||
def check_planned_qty(self):
|
||||
planned0 = (
|
||||
@@ -1358,7 +1357,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
wo_order = make_wo_order_test_record(item=fg_item, qty=2, skip_transfer=True)
|
||||
serial_nos = self.get_serial_nos_for_fg(wo_order.name)
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
|
||||
stock_entry.set_work_order_details()
|
||||
for row in stock_entry.items:
|
||||
if row.item_code == fg_item:
|
||||
@@ -1395,10 +1394,10 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
item.save()
|
||||
|
||||
try:
|
||||
wo_order = make_wo_order_test_record(item=fg_item, batch_size=5, qty=10, skip_transfer=True)
|
||||
wo_order = make_wo_order_test_record(item=fg_item, batch_size=1, qty=2, skip_transfer=True)
|
||||
serial_nos = self.get_serial_nos_for_fg(wo_order.name)
|
||||
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
|
||||
stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2))
|
||||
stock_entry.set_work_order_details()
|
||||
for row in stock_entry.items:
|
||||
if row.item_code == fg_item:
|
||||
@@ -2191,6 +2190,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertRaises(frappe.ValidationError, pick_list.submit)
|
||||
|
||||
def test_backflushed_batch_raw_materials_based_on_transferred(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2263,6 +2263,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertEqual(abs(d.qty), 2)
|
||||
|
||||
def test_backflushed_serial_no_raw_materials_based_on_transferred(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2310,6 +2311,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertEqual(manufacture_ste_doc2.items[0].qty, 3)
|
||||
|
||||
def test_backflushed_serial_no_batch_raw_materials_based_on_transferred(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2395,6 +2397,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertFalse(serial_nos)
|
||||
|
||||
def test_backflushed_batch_raw_materials_based_on_transferred_autosabb(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2461,6 +2464,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertEqual(manufacture_ste_doc.items[0].qty, 4.0)
|
||||
|
||||
def test_backflushed_serial_no_raw_materials_based_on_transferred_autosabb(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2528,6 +2532,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
self.assertEqual(manufacture_ste_doc.items[0].qty, 4.0)
|
||||
|
||||
def test_backflushed_serial_no_batch_raw_materials_based_on_transferred_autosabb(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
@@ -2586,6 +2591,7 @@ class TestWorkOrder(ERPNextTestSuite):
|
||||
|
||||
###
|
||||
def test_non_consumed_material_return_against_work_order(self):
|
||||
prepare_data_for_backflush_based_on_materials_transferred()
|
||||
frappe.db.set_single_value(
|
||||
"Manufacturing Settings",
|
||||
"backflush_raw_materials_based_on",
|
||||
|
||||
@@ -14,12 +14,10 @@ class TestProductionPlanningReport(ERPNextTestSuite):
|
||||
|
||||
wh = "_Test Warehouse - _TC"
|
||||
wo = make_wo_order_test_record(production_item="_Test FG Item", qty=2, source_warehouse=wh)
|
||||
self.addCleanup(self._cancel_and_delete, "Work Order", wo.name)
|
||||
|
||||
rm = wo.required_items[0].item_code
|
||||
for qty in (3, 4):
|
||||
po = create_purchase_order(item_code=rm, warehouse=wh, qty=qty, rate=10)
|
||||
self.addCleanup(self._cancel_and_delete, "Purchase Order", po.name)
|
||||
create_purchase_order(item_code=rm, warehouse=wh, qty=qty, rate=10)
|
||||
|
||||
filters = {
|
||||
"company": "_Test Company",
|
||||
@@ -34,14 +32,3 @@ class TestProductionPlanningReport(ERPNextTestSuite):
|
||||
self.assertTrue(rm_rows)
|
||||
# both on-order PO lines (3 + 4) are summed, not arbitrary-picked
|
||||
self.assertEqual(rm_rows[0]["arrival_qty"], 7)
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
import frappe
|
||||
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
@@ -5,7 +5,6 @@ import frappe
|
||||
from frappe.utils import add_days, nowdate
|
||||
|
||||
from erpnext.manufacturing.report.quality_inspection_summary.quality_inspection_summary import execute
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.quality_inspection.test_quality_inspection import (
|
||||
create_quality_inspection,
|
||||
make_minimal_job_card,
|
||||
@@ -16,7 +15,6 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
class TestQualityInspectionSummary(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
create_item("_Test Item")
|
||||
self.job_card = make_minimal_job_card(production_item="_Test Item")
|
||||
self.qi = create_quality_inspection(
|
||||
item_code="_Test Item",
|
||||
|
||||
@@ -568,7 +568,6 @@ class TestPlanAdapter(ERPNextTestSuite):
|
||||
frappe.get_doc(
|
||||
{"doctype": "Item Lead Time", "item_code": "Test PPS RM", "purchase_time": 2}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Item Lead Time", "Test PPS RM", force=True)
|
||||
|
||||
plan = self.make_plan()
|
||||
start_date = get_datetime("2026-11-02 09:00:00")
|
||||
@@ -636,7 +635,6 @@ class TestPlanAdapter(ERPNextTestSuite):
|
||||
],
|
||||
}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Item Lead Time", item_code, force=True)
|
||||
|
||||
@change_settings("Manufacturing Settings", {"mins_between_operations": 10, "allow_overtime": 0})
|
||||
def test_schedule_uses_supplier_wise_lead_time(self):
|
||||
|
||||
@@ -29,7 +29,7 @@ class TestActivityCost(ERPNextTestSuite):
|
||||
self.assertRaises(DuplicationError, activity_cost2.insert)
|
||||
|
||||
def test_default_activity_cost_title_and_duplication(self):
|
||||
activity_type = self._activity_type("_Test Default Cost Type")
|
||||
activity_type = "_Test Activity Type"
|
||||
|
||||
default_cost = frappe.get_doc(
|
||||
{
|
||||
@@ -46,7 +46,7 @@ class TestActivityCost(ERPNextTestSuite):
|
||||
self.assertRaises(DuplicationError, duplicate.insert)
|
||||
|
||||
def test_employee_name_and_title_are_set(self):
|
||||
activity_type = self._activity_type("_Test Employee Cost Type")
|
||||
activity_type = "_Test Activity Type"
|
||||
employee = frappe.db.get_all("Employee", filters={"first_name": "_Test Employee"})[0].name
|
||||
employee_name = frappe.db.get_value("Employee", employee, "employee_name")
|
||||
|
||||
@@ -62,8 +62,3 @@ class TestActivityCost(ERPNextTestSuite):
|
||||
).insert()
|
||||
self.assertEqual(cost.employee_name, employee_name)
|
||||
self.assertEqual(cost.title, f"{employee_name} for {activity_type}")
|
||||
|
||||
def _activity_type(self, name):
|
||||
if not frappe.db.exists("Activity Type", name):
|
||||
frappe.get_doc({"doctype": "Activity Type", "activity_type": name}).insert()
|
||||
return name
|
||||
|
||||
@@ -42,7 +42,6 @@ class TestProjectUpdate(ERPNextTestSuite):
|
||||
"time": "10:00:00",
|
||||
}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Project Update", pu.name, force=1)
|
||||
|
||||
# The converted update query (no longer referencing progress/progress_details) must find
|
||||
# yesterday's Project Update, keyed on project.name, on both engines.
|
||||
|
||||
@@ -400,7 +400,7 @@ class TestTimesheet(ERPNextTestSuite):
|
||||
customer = "_Test Customer"
|
||||
|
||||
# tie the current user (Administrator) to the customer so the portal resolves it
|
||||
contact = frappe.get_doc(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Contact",
|
||||
"first_name": "_Test Timesheet Portal Contact",
|
||||
@@ -408,7 +408,6 @@ class TestTimesheet(ERPNextTestSuite):
|
||||
"links": [{"link_doctype": "Customer", "link_name": customer}],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
self.addCleanup(self._delete_if_exists, "Contact", contact.name)
|
||||
|
||||
si = create_sales_invoice(customer=customer)
|
||||
|
||||
@@ -464,11 +463,6 @@ class TestTimesheet(ERPNextTestSuite):
|
||||
timesheet.save()
|
||||
self.assertEqual(timesheet.get_title(), frappe.db.get_value("Employee", second, "employee_name"))
|
||||
|
||||
@staticmethod
|
||||
def _delete_if_exists(doctype, name):
|
||||
if frappe.db.exists(doctype, name):
|
||||
frappe.delete_doc(doctype, name, force=True)
|
||||
|
||||
|
||||
def make_timesheet(
|
||||
employee,
|
||||
|
||||
@@ -29,29 +29,30 @@ class TestCustomer(ERPNextTestSuite):
|
||||
company_currency = frappe.get_cached_value("Company", company, "default_currency")
|
||||
foreign_currency = "USD" if company_currency != "USD" else "EUR"
|
||||
|
||||
original_company = frappe.defaults.get_user_default("company")
|
||||
frappe.defaults.set_user_default("company", company)
|
||||
self.addCleanup(frappe.defaults.clear_user_default, "company")
|
||||
try:
|
||||
# Master data seeds a current-dated exchange rate, so make_quotation should
|
||||
# resolve that rate instead of falling back to the default conversion rate of 1.0.
|
||||
expected_rate = get_exchange_rate(foreign_currency, company_currency, nowdate())
|
||||
|
||||
# Master data seeds a current-dated exchange rate, so make_quotation should
|
||||
# resolve that rate instead of falling back to the default conversion rate of 1.0.
|
||||
expected_rate = get_exchange_rate(foreign_currency, company_currency, nowdate())
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Customer",
|
||||
"customer_name": "_Test Customer FX Quotation",
|
||||
"customer_type": "Company",
|
||||
"default_currency": foreign_currency,
|
||||
}
|
||||
).insert()
|
||||
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Customer",
|
||||
"customer_name": "_Test Customer FX Quotation",
|
||||
"customer_type": "Company",
|
||||
"default_currency": foreign_currency,
|
||||
}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Customer", customer.name, force=1)
|
||||
quotation = make_quotation(customer.name)
|
||||
|
||||
quotation = make_quotation(customer.name)
|
||||
|
||||
self.assertEqual(quotation.currency, foreign_currency)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 1.0)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 0.0)
|
||||
self.assertEqual(flt(quotation.conversion_rate), flt(expected_rate))
|
||||
self.assertEqual(quotation.currency, foreign_currency)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 1.0)
|
||||
self.assertNotEqual(flt(quotation.conversion_rate), 0.0)
|
||||
self.assertEqual(flt(quotation.conversion_rate), flt(expected_rate))
|
||||
finally:
|
||||
frappe.defaults.set_user_default("company", original_company)
|
||||
|
||||
def test_get_customer_name_dedupes_with_numeric_suffix(self):
|
||||
# When a customer name already exists, get_customer_name appends "- <max suffix + 1>". The
|
||||
@@ -63,7 +64,6 @@ class TestCustomer(ERPNextTestSuite):
|
||||
frappe.get_doc(
|
||||
{"doctype": "Customer", "customer_name": nm, "customer_type": "Individual"}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Customer", nm, force=1)
|
||||
|
||||
doc = frappe.get_doc({"doctype": "Customer", "customer_name": base, "customer_type": "Individual"})
|
||||
self.assertEqual(doc.get_customer_name(), f"{base} - 4")
|
||||
@@ -79,7 +79,6 @@ class TestCustomer(ERPNextTestSuite):
|
||||
frappe.get_doc(
|
||||
{"doctype": "Customer", "customer_name": nm, "customer_type": "Individual"}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Customer", nm, force=1)
|
||||
|
||||
doc = frappe.get_doc({"doctype": "Customer", "customer_name": base, "customer_type": "Individual"})
|
||||
self.assertEqual(doc.get_customer_name(), f"{base} - 4")
|
||||
@@ -510,9 +509,6 @@ class TestCustomer(ERPNextTestSuite):
|
||||
def test_overdue_billing_threshold_falls_back_to_customer_group(self):
|
||||
customer_group = frappe.get_cached_value("Customer", "_Test Customer", "customer_group")
|
||||
group = frappe.get_doc("Customer Group", customer_group)
|
||||
customer = frappe.get_doc("Customer", "_Test Customer")
|
||||
self._restore_credit_limits_after(group)
|
||||
self._restore_credit_limits_after(customer)
|
||||
|
||||
group.credit_limits = []
|
||||
group.append("credit_limits", {"company": "_Test Company", "overdue_billing_threshold": 5000})
|
||||
@@ -529,18 +525,6 @@ class TestCustomer(ERPNextTestSuite):
|
||||
set_overdue_billing_threshold("_Test Customer", "_Test Company", 0)
|
||||
self.assertEqual(get_overdue_billing_threshold("_Test Customer", "_Test Company"), 5000)
|
||||
|
||||
def _restore_credit_limits_after(self, doc):
|
||||
original = [row.as_dict(no_default_fields=True) for row in doc.credit_limits]
|
||||
|
||||
def restore():
|
||||
fresh = frappe.get_doc(doc.doctype, doc.name)
|
||||
fresh.credit_limits = []
|
||||
for row in original:
|
||||
fresh.append("credit_limits", row)
|
||||
fresh.save()
|
||||
|
||||
self.addCleanup(restore)
|
||||
|
||||
def test_overdue_threshold_row_without_credit_limit(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import json
|
||||
from contextlib import nullcontext
|
||||
from io import BytesIO
|
||||
from unittest.mock import patch
|
||||
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from pypdf import PdfWriter
|
||||
|
||||
from erpnext.selling.doctype.proforma_invoice.proforma_invoice import (
|
||||
get_sales_order_items,
|
||||
@@ -15,13 +19,34 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
def _make_test_pdf():
|
||||
content = BytesIO()
|
||||
writer = PdfWriter()
|
||||
writer.add_blank_page(width=72, height=72)
|
||||
writer.write(content)
|
||||
return content.getvalue()
|
||||
|
||||
|
||||
TEST_PDF = _make_test_pdf()
|
||||
|
||||
|
||||
class TestProformaInvoice(ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
frappe.db.set_single_value("Selling Settings", "enable_proforma_invoice", 1)
|
||||
|
||||
def create_proforma(self, sales_order, lines, **kwargs):
|
||||
items = [{"so_detail": so_detail, "qty": qty} for so_detail, qty in lines]
|
||||
name = make_proforma_invoice(sales_order.name, json.dumps(items), **kwargs)
|
||||
def create_proforma(self, sales_order, lines, use_real_pdf_renderer=False, **kwargs):
|
||||
items = [line if isinstance(line, dict) else {"so_detail": line[0], "qty": line[1]} for line in lines]
|
||||
pdf_renderer = (
|
||||
nullcontext()
|
||||
if use_real_pdf_renderer
|
||||
else patch.object(
|
||||
frappe,
|
||||
"attach_print",
|
||||
return_value={"fname": "proforma.pdf", "fcontent": TEST_PDF},
|
||||
)
|
||||
)
|
||||
with pdf_renderer:
|
||||
name = make_proforma_invoice(sales_order.name, json.dumps(items), **kwargs)
|
||||
return frappe.get_doc("Proforma Invoice", name)
|
||||
|
||||
def test_partial_proforma_is_non_blocking(self):
|
||||
@@ -29,7 +54,7 @@ class TestProformaInvoice(ERPNextTestSuite):
|
||||
sales_order = make_sales_order(qty=10)
|
||||
so_detail = sales_order.items[0].name
|
||||
|
||||
proforma = self.create_proforma(sales_order, [(so_detail, 4)])
|
||||
proforma = self.create_proforma(sales_order, [(so_detail, 4)], use_real_pdf_renderer=True)
|
||||
|
||||
self.assertEqual(proforma.status, "Issued")
|
||||
self.assertEqual(proforma.docstatus, 1)
|
||||
@@ -70,12 +95,11 @@ class TestProformaInvoice(ERPNextTestSuite):
|
||||
sales_order = make_sales_order(qty=10) # rate 100
|
||||
so_detail = sales_order.items[0].name
|
||||
|
||||
name = make_proforma_invoice(
|
||||
sales_order.name,
|
||||
json.dumps([{"so_detail": so_detail, "qty": 5, "amount": 250}]),
|
||||
proforma = self.create_proforma(
|
||||
sales_order,
|
||||
[{"so_detail": so_detail, "qty": 5, "amount": 250}],
|
||||
based_on="Amount",
|
||||
)
|
||||
proforma = frappe.get_doc("Proforma Invoice", name)
|
||||
|
||||
self.assertEqual(proforma.based_on, "Amount")
|
||||
item = proforma.items[0]
|
||||
@@ -117,22 +141,22 @@ class TestProformaInvoice(ERPNextTestSuite):
|
||||
sales_order = make_sales_order(qty=10)
|
||||
so_detail = sales_order.items[0].name
|
||||
|
||||
amount_based = make_proforma_invoice(
|
||||
sales_order.name,
|
||||
json.dumps([{"so_detail": so_detail, "qty": 5, "amount": 250}]),
|
||||
amount_based = self.create_proforma(
|
||||
sales_order,
|
||||
[{"so_detail": so_detail, "qty": 5, "amount": 250}],
|
||||
based_on="Amount",
|
||||
hide_item_qty=1,
|
||||
)
|
||||
self.assertEqual(frappe.db.get_value("Proforma Invoice", amount_based, "hide_item_qty"), 1)
|
||||
self.assertEqual(amount_based.hide_item_qty, 1)
|
||||
|
||||
# ignored outside Amount basis
|
||||
qty_based = make_proforma_invoice(
|
||||
sales_order.name,
|
||||
json.dumps([{"so_detail": so_detail, "qty": 4}]),
|
||||
qty_based = self.create_proforma(
|
||||
sales_order,
|
||||
[(so_detail, 4)],
|
||||
based_on="Quantity",
|
||||
hide_item_qty=1,
|
||||
)
|
||||
self.assertEqual(frappe.db.get_value("Proforma Invoice", qty_based, "hide_item_qty"), 0)
|
||||
self.assertEqual(qty_based.hide_item_qty, 0)
|
||||
|
||||
def test_feature_toggle_is_enforced(self):
|
||||
sales_order = make_sales_order(qty=10)
|
||||
|
||||
@@ -19,7 +19,7 @@ class TestQuotation(ERPNextTestSuite):
|
||||
def test_update_child_quotation_add_item(self):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
item_1 = make_item("_Test Item")
|
||||
item_1 = frappe.get_doc("Item", "_Test Item")
|
||||
item_2 = make_item("_Test Item 1")
|
||||
|
||||
item_list = [
|
||||
@@ -63,7 +63,7 @@ class TestQuotation(ERPNextTestSuite):
|
||||
def test_update_child_rate_change(self):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
item_1 = make_item("_Test Item")
|
||||
item_1 = frappe.get_doc("Item", "_Test Item")
|
||||
item_2 = make_item("_Test Item 1")
|
||||
|
||||
item_list = [
|
||||
@@ -924,13 +924,8 @@ class TestQuotation(ERPNextTestSuite):
|
||||
item = "_Test Item FOR UOM Validation"
|
||||
make_item(item, {"is_stock_item": 1})
|
||||
|
||||
if not frappe.db.exists("UOM", "lbs"):
|
||||
frappe.get_doc({"doctype": "UOM", "uom_name": "lbs", "must_be_whole_number": 1}).insert()
|
||||
else:
|
||||
frappe.db.set_value("UOM", "lbs", "must_be_whole_number", 1)
|
||||
|
||||
quotation = make_quotation(item_code=item, qty=1, rate=100, do_not_submit=1)
|
||||
quotation.items[0].uom = "lbs"
|
||||
quotation.items[0].uom = "_Test UOM"
|
||||
quotation.items[0].conversion_factor = 2.23
|
||||
self.assertRaises(frappe.ValidationError, quotation.save)
|
||||
|
||||
@@ -1007,7 +1002,6 @@ class TestQuotation(ERPNextTestSuite):
|
||||
from erpnext.selling.doctype.quotation.mapper import make_sales_order
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
make_item("_Test Item 2", {"is_stock_item": 1})
|
||||
quotation = make_quotation(qty=0, do_not_save=1)
|
||||
quotation.append("items", {"item_code": "_Test Item 2", "qty": 10, "rate": 100})
|
||||
quotation.submit()
|
||||
@@ -1041,8 +1035,6 @@ class TestQuotation(ERPNextTestSuite):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
# item code same but description different
|
||||
make_item("_Test Item 2", {"is_stock_item": 1})
|
||||
|
||||
quotation = make_quotation(qty=10, rate=100, do_not_submit=1)
|
||||
|
||||
# duplicate items
|
||||
|
||||
@@ -82,8 +82,10 @@ class TestSalesOrder(ERPNextTestSuite):
|
||||
self.assertEqual(frappe.db.get_value("Item Price", all_item_prices[0].name, "price_list_rate"), 1000)
|
||||
|
||||
def test_sales_order_with_product_bundle_for_partial_material_request(self):
|
||||
product_bundle = make_product_bundle(
|
||||
"_Test Product Bundle Item", ["_Test Item", "_Test Item Home Desktop 100"]
|
||||
from erpnext.selling.doctype.product_bundle.product_bundle import get_active_product_bundle
|
||||
|
||||
product_bundle = frappe.get_doc(
|
||||
"Product Bundle", get_active_product_bundle("_Test Product Bundle Item")
|
||||
)
|
||||
so = make_sales_order(item_code=product_bundle.new_item_code, qty=2)
|
||||
mr = make_material_request(so.name)
|
||||
@@ -274,10 +276,10 @@ class TestSalesOrder(ERPNextTestSuite):
|
||||
so.append("items", {"item_code": "_Test Item 2", "qty": 1, "rate": -10})
|
||||
so.save()
|
||||
|
||||
with self.assertRaises(frappe.ValidationError) as error:
|
||||
with self.assertRaises(frappe.ValidationError):
|
||||
so.submit()
|
||||
|
||||
self.assertIn("selling-settings", str(error.exception))
|
||||
self.assertIn("selling-settings", frappe.local.message_log[-1]["message"])
|
||||
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"allow_negative_rates_for_items": 1})
|
||||
def test_sales_order_negative_rate_setting_does_not_allow_negative_quantity(self):
|
||||
@@ -872,9 +874,7 @@ class TestSalesOrder(ERPNextTestSuite):
|
||||
existing_item = so.get("items")[0]
|
||||
|
||||
# a company gets a default warehouse when its warehouses are created
|
||||
company_default = frappe.db.get_value("Company", so.company, "default_warehouse")
|
||||
frappe.db.set_value("Company", so.company, "default_warehouse", None)
|
||||
self.addCleanup(frappe.db.set_value, "Company", so.company, "default_warehouse", company_default)
|
||||
|
||||
def get_trans_items(warehouse=None):
|
||||
new_row = {"item_code": item_code, "rate": 200, "qty": 7}
|
||||
|
||||
@@ -17,13 +17,10 @@ class TestLostQuotations(ERPNextTestSuite):
|
||||
def test_lost_quotations_percentage_is_not_integer_divided(self):
|
||||
# `lost_quotations_pct` is count(group) / count(total) * 100. count/count is integer division on
|
||||
# Postgres, which truncates a proper fraction to 0; this asserts the percentage stays fractional.
|
||||
quotations = []
|
||||
# reason A on one quotation, reason B on three -> A is a strict minority of the total
|
||||
quotations.append(self._make_lost_quotation(self.reason_a))
|
||||
self._make_lost_quotation(self.reason_a)
|
||||
for _ in range(3):
|
||||
quotations.append(self._make_lost_quotation(self.reason_b))
|
||||
for qo in quotations:
|
||||
self.addCleanup(self._cancel_and_delete, qo.name)
|
||||
self._make_lost_quotation(self.reason_b)
|
||||
|
||||
_columns, data = execute(
|
||||
frappe._dict({"company": self.company, "timespan": "This Year", "group_by": "Lost Reason"})
|
||||
@@ -37,27 +34,11 @@ class TestLostQuotations(ERPNextTestSuite):
|
||||
self.assertLess(row_a[2], 100)
|
||||
|
||||
def _ensure_lost_reason(self, name):
|
||||
# only clean up reasons this test created, so a pre-existing master is left intact
|
||||
if not frappe.db.exists("Quotation Lost Reason", name):
|
||||
frappe.get_doc({"doctype": "Quotation Lost Reason", "order_lost_reason": name}).insert()
|
||||
self.addCleanup(self._delete_lost_reason, name)
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def _delete_lost_reason(name):
|
||||
if frappe.db.exists("Quotation Lost Reason", name):
|
||||
frappe.delete_doc("Quotation Lost Reason", name, force=1)
|
||||
|
||||
def _make_lost_quotation(self, reason):
|
||||
qo = make_quotation(company=self.company, qty=1, rate=100)
|
||||
qo.declare_enquiry_lost([{"lost_reason": reason}], [])
|
||||
return qo
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete(name):
|
||||
if not frappe.db.exists("Quotation", name):
|
||||
return
|
||||
doc = frappe.get_doc("Quotation", name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc("Quotation", name, force=1)
|
||||
|
||||
@@ -135,7 +135,7 @@ def make_transactions(company):
|
||||
for item in json.loads(data):
|
||||
create_transaction(item, company, start_date)
|
||||
|
||||
convert_order_to_invoices()
|
||||
convert_order_to_invoices(company)
|
||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0)
|
||||
|
||||
|
||||
@@ -164,12 +164,15 @@ def create_transaction(doctype, company, start_date):
|
||||
doc.submit()
|
||||
|
||||
|
||||
def convert_order_to_invoices():
|
||||
def convert_order_to_invoices(company):
|
||||
for document in ["Purchase Order", "Sales Order"]:
|
||||
# Keep some orders intentionally unbilled/unpaid
|
||||
for i, order in enumerate(
|
||||
frappe.db.get_all(
|
||||
document, filters={"docstatus": 1}, fields=["name", "transaction_date"], limit=6
|
||||
document,
|
||||
filters={"docstatus": 1, "company": company},
|
||||
fields=["name", "transaction_date"],
|
||||
limit=6,
|
||||
)
|
||||
):
|
||||
if document == "Purchase Order":
|
||||
|
||||
@@ -27,7 +27,7 @@ class TestAuthorizationControl(ERPNextTestSuite):
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
rule = frappe.get_doc(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Authorization Rule",
|
||||
"transaction": "Sales Order",
|
||||
@@ -37,19 +37,17 @@ class TestAuthorizationControl(ERPNextTestSuite):
|
||||
"approving_role": "_Test Approver Role",
|
||||
}
|
||||
).insert()
|
||||
self.addCleanup(frappe.delete_doc, "Authorization Rule", rule.name, force=1)
|
||||
|
||||
controller = frappe.get_cached_doc("Authorization Control")
|
||||
frappe.set_user(user)
|
||||
self.addCleanup(frappe.set_user, "Administrator")
|
||||
# User lacks _Test Approver Role and the total exceeds the rule value -> not authorized.
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
controller.validate_approving_authority,
|
||||
"Sales Order",
|
||||
"_Test Company",
|
||||
5000,
|
||||
)
|
||||
with self.set_user(user):
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
controller.validate_approving_authority,
|
||||
"Sales Order",
|
||||
"_Test Company",
|
||||
5000,
|
||||
)
|
||||
|
||||
def test_get_value_based_rule_runs(self):
|
||||
# Exercises the four query-builder lookups (incl. the Employee designation subquery) added in
|
||||
|
||||
@@ -161,12 +161,10 @@ class TestCompany(ERPNextTestSuite):
|
||||
}
|
||||
)
|
||||
secondary.insert()
|
||||
self.addCleanup(secondary.delete)
|
||||
|
||||
primary = frappe.copy_doc(secondary)
|
||||
primary.is_primary_address = 1
|
||||
primary.insert()
|
||||
self.addCleanup(primary.delete)
|
||||
|
||||
self.assertEqual(get_default_company_address(company), primary.name)
|
||||
|
||||
@@ -236,12 +234,8 @@ class TestCompany(ERPNextTestSuite):
|
||||
|
||||
company = "_Test Company"
|
||||
cd = frappe.qb.DocType("Company")
|
||||
original = frappe.db.get_value("Company", company, "parent_company")
|
||||
# force '' (not NULL) at the SQL layer, bypassing frappe's empty -> NULL doc coercion
|
||||
frappe.qb.update(cd).set(cd.parent_company, "").where(cd.name == company).run()
|
||||
self.addCleanup(
|
||||
lambda: frappe.qb.update(cd).set(cd.parent_company, original).where(cd.name == company).run()
|
||||
)
|
||||
|
||||
roots = {row.value for row in get_children("Company", parent="")}
|
||||
self.assertIn(company, roots)
|
||||
@@ -262,10 +256,8 @@ class TestCompany(ERPNextTestSuite):
|
||||
|
||||
before = get_all_transactions_annual_history(company).get(key, 0)
|
||||
|
||||
quotation = make_quotation(company=company, transaction_date=txn_date, do_not_submit=True)
|
||||
self.addCleanup(frappe.delete_doc, "Quotation", quotation.name, force=True)
|
||||
sales_order = make_sales_order(company=company, transaction_date=txn_date, do_not_submit=True)
|
||||
self.addCleanup(frappe.delete_doc, "Sales Order", sales_order.name, force=True)
|
||||
make_quotation(company=company, transaction_date=txn_date, do_not_submit=True)
|
||||
make_sales_order(company=company, transaction_date=txn_date, do_not_submit=True)
|
||||
|
||||
after = get_all_transactions_annual_history(company).get(key, 0)
|
||||
self.assertEqual(after - before, 2)
|
||||
|
||||
@@ -5,6 +5,7 @@ import frappe
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.utils import _create_bin
|
||||
from erpnext.tests.assertions import assert_raises_with_savepoint
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -19,10 +20,8 @@ class TestBin(ERPNextTestSuite):
|
||||
bin1.insert()
|
||||
|
||||
bin2 = frappe.get_doc(doctype="Bin", item_code=item_code, warehouse=warehouse)
|
||||
frappe.db.savepoint("dup_bin")
|
||||
with self.assertRaises(frappe.UniqueValidationError):
|
||||
with assert_raises_with_savepoint(self, frappe.UniqueValidationError):
|
||||
bin2.insert()
|
||||
frappe.db.rollback(save_point="dup_bin") # preserve transaction in postgres
|
||||
|
||||
# util method should handle it
|
||||
bin = _create_bin(item_code, warehouse)
|
||||
|
||||
@@ -108,25 +108,23 @@ class TestCompanyRestriction(ERPNextTestSuite):
|
||||
frappe.get_doc({"doctype": "User Permission", **permission}).insert(ignore_permissions=True)
|
||||
frappe.clear_cache(user=user)
|
||||
|
||||
frappe.set_user(user)
|
||||
self.addCleanup(frappe.set_user, "Administrator")
|
||||
with self.set_user(user):
|
||||
results = party_query(
|
||||
"Customer",
|
||||
customer,
|
||||
"name",
|
||||
0,
|
||||
20,
|
||||
filters={"disabled": 0, "company": "_Test Company"},
|
||||
)
|
||||
self.assertIn(customer, [row[0] for row in results])
|
||||
|
||||
results = party_query(
|
||||
"Customer",
|
||||
customer,
|
||||
"name",
|
||||
0,
|
||||
20,
|
||||
filters={"disabled": 0, "company": "_Test Company"},
|
||||
)
|
||||
self.assertIn(customer, [row[0] for row in results])
|
||||
|
||||
details = get_party_details(
|
||||
party=customer,
|
||||
party_type="Customer",
|
||||
company="_Test Company",
|
||||
)
|
||||
self.assertEqual(details.customer, customer)
|
||||
details = get_party_details(
|
||||
party=customer,
|
||||
party_type="Customer",
|
||||
company="_Test Company",
|
||||
)
|
||||
self.assertEqual(details.customer, customer)
|
||||
|
||||
def test_unrestricted_item_is_not_blocked(self):
|
||||
item = make_item()
|
||||
@@ -184,14 +182,12 @@ class TestCompanyRestriction(ERPNextTestSuite):
|
||||
permitted = frappe.get_meta("Customer").get_permitted_fieldnames(user=manager)
|
||||
self.assertIn("restrict_to_companies", permitted)
|
||||
|
||||
frappe.set_user(sales_user)
|
||||
self.addCleanup(frappe.set_user, "Administrator")
|
||||
with self.set_user(sales_user):
|
||||
doc = frappe.get_doc("Customer", customer)
|
||||
doc.restrict_to_companies = 0
|
||||
doc.set("allowed_companies", [])
|
||||
doc.save()
|
||||
|
||||
doc = frappe.get_doc("Customer", customer)
|
||||
doc.restrict_to_companies = 0
|
||||
doc.set("allowed_companies", [])
|
||||
doc.save()
|
||||
|
||||
doc.reload()
|
||||
self.assertEqual(doc.restrict_to_companies, 1)
|
||||
self.assertEqual([row.company for row in doc.allowed_companies], ["_Test Company"])
|
||||
doc.reload()
|
||||
self.assertEqual(doc.restrict_to_companies, 1)
|
||||
self.assertEqual([row.company for row in doc.allowed_companies], ["_Test Company"])
|
||||
|
||||
@@ -910,14 +910,8 @@ class TestDeliveryNote(ERPNextTestSuite):
|
||||
self.assertEqual(sn.warehouse, warehouse)
|
||||
|
||||
def test_delivery_of_bundled_items_to_target_warehouse(self):
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
|
||||
customer_name = 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",
|
||||
)
|
||||
customer_name = "_Test Internal Customer 2"
|
||||
|
||||
set_valuation_method("_Test Item", "FIFO")
|
||||
set_valuation_method("_Test Item Home Desktop 100", "FIFO")
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestDeliveryTrip(ERPNextTestSuite):
|
||||
"password": "test",
|
||||
"smtp_server": "localhost",
|
||||
"stmp_port": 25,
|
||||
"email_id": "test@example.in",
|
||||
"email_id": f"delivery-trip-{frappe.generate_hash(length=10)}@example.in",
|
||||
}
|
||||
)
|
||||
outgoing.save()
|
||||
|
||||
@@ -443,6 +443,8 @@ class TestInventoryDimension(ERPNextTestSuite):
|
||||
document_type="Inv Site",
|
||||
validate_negative_stock=1,
|
||||
)
|
||||
inv_dimension.db_set("validate_negative_stock", 1)
|
||||
frappe.clear_cache(doctype="Inventory Dimension")
|
||||
|
||||
warehouse = create_warehouse("Negative Stock Warehouse")
|
||||
|
||||
@@ -758,24 +760,13 @@ def create_inventory_dimension(**args):
|
||||
|
||||
|
||||
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 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
|
||||
supplier = create_internal_supplier(
|
||||
"_Test Internal Supplier 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
customer = "_Test Internal Customer 2"
|
||||
supplier = "_Test Internal Supplier 2"
|
||||
|
||||
for store in ["Inter Transfer Store 1", "Inter Transfer Store 2", "Inter Transfer Store 3"]:
|
||||
if not frappe.db.exists("Store", store):
|
||||
|
||||
@@ -25,7 +25,8 @@ from erpnext.stock.doctype.item.item import (
|
||||
validate_is_stock_item,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
from erpnext.stock.get_item_details import get_item_details, get_item_tax_map, get_item_tax_template
|
||||
from erpnext.tests.assertions import assert_raises_with_savepoint
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -303,28 +304,35 @@ class TestItem(ERPNextTestSuite):
|
||||
},
|
||||
}
|
||||
|
||||
for data in expected_item_tax_template:
|
||||
details = get_item_details(
|
||||
frappe._dict(
|
||||
{
|
||||
"item_code": data["item_code"],
|
||||
"tax_category": data["tax_category"],
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "_Test Currency",
|
||||
"doctype": "Sales Order",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"conversion_factor": 1,
|
||||
"price_list_uom_dependant": 1,
|
||||
"ignore_pricing_rule": 1,
|
||||
}
|
||||
)
|
||||
for index, data in enumerate(expected_item_tax_template):
|
||||
ctx = frappe._dict(
|
||||
{
|
||||
"item_code": data["item_code"],
|
||||
"tax_category": data["tax_category"],
|
||||
"company": "_Test Company",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "_Test Currency",
|
||||
"doctype": "Sales Order",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"order_type": "Sales",
|
||||
"customer": "_Test Customer",
|
||||
"conversion_factor": 1,
|
||||
"price_list_uom_dependant": 1,
|
||||
"ignore_pricing_rule": 1,
|
||||
}
|
||||
)
|
||||
|
||||
if index == 0:
|
||||
details = get_item_details(ctx)
|
||||
else:
|
||||
details = frappe._dict()
|
||||
get_item_tax_template(ctx, out=details)
|
||||
details.item_tax_rate = get_item_tax_map(
|
||||
doc=ctx, tax_template=details.item_tax_template, as_json=True
|
||||
)
|
||||
|
||||
self.assertEqual(details.item_tax_template, data["item_tax_template"])
|
||||
self.assertEqual(
|
||||
json.loads(details.item_tax_rate), expected_item_tax_map[details.item_tax_template]
|
||||
@@ -488,17 +496,6 @@ class TestItem(ERPNextTestSuite):
|
||||
row.attribute_value = "Larger"
|
||||
break
|
||||
|
||||
def restore_test_size_large():
|
||||
doc = frappe.get_doc("Item Attribute", "Test Size")
|
||||
for row in doc.item_attribute_values:
|
||||
if row.attribute_value == "Larger":
|
||||
row.attribute_value = "Large"
|
||||
break
|
||||
frappe.flags.attribute_values = None
|
||||
doc.save()
|
||||
|
||||
self.addCleanup(restore_test_size_large)
|
||||
|
||||
frappe.flags.attribute_values = None
|
||||
attribute.save()
|
||||
|
||||
@@ -522,16 +519,6 @@ class TestItem(ERPNextTestSuite):
|
||||
small_variant.save()
|
||||
|
||||
attribute = frappe.get_doc("Item Attribute", "Test Size")
|
||||
original_values = {row.name: row.attribute_value for row in attribute.item_attribute_values}
|
||||
|
||||
def restore_test_size_values():
|
||||
doc = frappe.get_doc("Item Attribute", "Test Size")
|
||||
for row in doc.item_attribute_values:
|
||||
row.attribute_value = original_values[row.name]
|
||||
frappe.flags.attribute_values = None
|
||||
doc.save()
|
||||
|
||||
self.addCleanup(restore_test_size_values)
|
||||
|
||||
for row in attribute.item_attribute_values:
|
||||
if row.attribute_value == "Large":
|
||||
@@ -572,18 +559,6 @@ class TestItem(ERPNextTestSuite):
|
||||
row.abbr = "LRG"
|
||||
break
|
||||
|
||||
def restore_test_size_abbr():
|
||||
doc = frappe.get_doc("Item Attribute", "Test Size")
|
||||
for row in doc.item_attribute_values:
|
||||
if row.attribute_value == "Large":
|
||||
row.abbr = "L"
|
||||
break
|
||||
frappe.flags.attribute_values = None
|
||||
doc.save()
|
||||
|
||||
self.addCleanup(restore_test_size_abbr)
|
||||
self.addCleanup(lambda: frappe.delete_doc_if_exists("Item", "_Test Variant Item-LRG", force=1))
|
||||
|
||||
frappe.flags.attribute_values = None
|
||||
attribute.save()
|
||||
|
||||
@@ -615,7 +590,6 @@ class TestItem(ERPNextTestSuite):
|
||||
}
|
||||
)
|
||||
template.insert()
|
||||
self.addCleanup(lambda: frappe.delete_doc_if_exists("Item", "_Test Variant Item Diff", force=1))
|
||||
|
||||
variant = create_variant("_Test Variant Item Diff", {"Test Size": "Large"})
|
||||
variant.save()
|
||||
@@ -632,18 +606,6 @@ class TestItem(ERPNextTestSuite):
|
||||
row.abbr = "LRG"
|
||||
break
|
||||
|
||||
def restore_test_size_abbr():
|
||||
doc = frappe.get_doc("Item Attribute", "Test Size")
|
||||
for row in doc.item_attribute_values:
|
||||
if row.attribute_value == "Large":
|
||||
row.abbr = "L"
|
||||
break
|
||||
frappe.flags.attribute_values = None
|
||||
doc.save()
|
||||
|
||||
self.addCleanup(restore_test_size_abbr)
|
||||
self.addCleanup(lambda: frappe.delete_doc_if_exists("Item", "_Test Variant Item Diff-LRG", force=1))
|
||||
|
||||
frappe.flags.attribute_values = None
|
||||
attribute.save()
|
||||
|
||||
@@ -934,9 +896,8 @@ class TestItem(ERPNextTestSuite):
|
||||
item_doc = frappe.get_doc("Item", item_code)
|
||||
new_barcode = item_doc.append("barcodes")
|
||||
new_barcode.update(barcode_properties_list[0])
|
||||
frappe.db.savepoint("dup_barcode")
|
||||
self.assertRaises(frappe.UniqueValidationError, item_doc.save)
|
||||
frappe.db.rollback(save_point="dup_barcode") # preserve transaction in postgres
|
||||
with assert_raises_with_savepoint(self, frappe.UniqueValidationError):
|
||||
item_doc.save()
|
||||
|
||||
# Add invalid barcode - should cause InvalidBarcode
|
||||
item_doc = frappe.get_doc("Item", item_code)
|
||||
@@ -1260,13 +1221,13 @@ class TestItem(ERPNextTestSuite):
|
||||
items = {
|
||||
"Test Opening Stock for Serial No": {
|
||||
"has_serial_no": 1,
|
||||
"opening_stock": 5,
|
||||
"opening_stock": 1,
|
||||
"serial_no_series": "SN-TOPN-.####",
|
||||
"valuation_rate": 100,
|
||||
},
|
||||
"Test Opening Stock for Batch No": {
|
||||
"has_batch_no": 1,
|
||||
"opening_stock": 5,
|
||||
"opening_stock": 1,
|
||||
"batch_number_series": "BCH-TOPN-.####",
|
||||
"valuation_rate": 100,
|
||||
"create_new_batch": 1,
|
||||
@@ -1274,7 +1235,7 @@ class TestItem(ERPNextTestSuite):
|
||||
"Test Opening Stock for Serial and Batch No": {
|
||||
"has_serial_no": 1,
|
||||
"has_batch_no": 1,
|
||||
"opening_stock": 5,
|
||||
"opening_stock": 1,
|
||||
"batch_number_series": "SN-BCH-TOPN-.####",
|
||||
"serial_no_series": "BCH-SN-TOPN-.####",
|
||||
"valuation_rate": 100,
|
||||
|
||||
@@ -40,7 +40,6 @@ class TestItemAttribute(ERPNextTestSuite):
|
||||
frappe.delete_doc_if_exists("Item", "_Test Variant Item-L", force=1)
|
||||
variant = create_variant("_Test Variant Item", {"Test Size": "Large"})
|
||||
variant.save()
|
||||
self.addCleanup(frappe.delete_doc_if_exists, "Item", "_Test Variant Item-L", force=1)
|
||||
|
||||
attribute = frappe.get_doc("Item Attribute", "Test Size")
|
||||
attribute.item_attribute_values = []
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
|
||||
import copy
|
||||
from unittest.mock import patch
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_days, add_to_date, flt, now, nowtime, today
|
||||
@@ -33,22 +34,12 @@ class TestLandedCostVoucher(ERPNextTestSuite):
|
||||
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import get_vendor_invoices
|
||||
|
||||
pi = make_purchase_invoice(item_code="_Test Non Stock Item", qty=1, rate=100)
|
||||
self.addCleanup(self._cancel_and_delete_pi, pi.name)
|
||||
|
||||
rows = get_vendor_invoices(
|
||||
"Purchase Invoice", "", "name", 0, 20, {"company": "_Test Company", "name": pi.name}
|
||||
)
|
||||
self.assertTrue(any(r[0] == pi.name for r in rows))
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete_pi(name):
|
||||
if not frappe.db.exists("Purchase Invoice", name):
|
||||
return
|
||||
doc = frappe.get_doc("Purchase Invoice", name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc("Purchase Invoice", name, force=1)
|
||||
|
||||
def test_landed_cost_voucher(self):
|
||||
frappe.db.set_single_value("Buying Settings", "allow_multiple_items", 1)
|
||||
|
||||
@@ -1333,6 +1324,7 @@ class TestLandedCostVoucher(ERPNextTestSuite):
|
||||
|
||||
self.assertFalse(gl_entries)
|
||||
|
||||
@patch.dict(frappe.flags, {"dont_execute_stock_reposts": True})
|
||||
def test_landed_cost_voucher_does_not_change_qty_across_stock_reco(self):
|
||||
"""LCV cost updates must not change quantity after a batch stock reconciliation."""
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
@@ -1348,10 +1340,6 @@ class TestLandedCostVoucher(ERPNextTestSuite):
|
||||
first_batch = frappe.get_doc({"doctype": "Batch", "item": item}).insert().name
|
||||
second_batch = frappe.get_doc({"doctype": "Batch", "item": item}).insert().name
|
||||
|
||||
# Inspect the immediate LCV result before a queued repost repairs it.
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts", None)
|
||||
|
||||
receipt = make_purchase_receipt(
|
||||
company=company,
|
||||
warehouse=warehouse,
|
||||
@@ -1603,35 +1591,16 @@ class TestLandedCostVoucherAccountingDimensions(ERPNextTestSuite):
|
||||
dimension = frappe.get_doc("Accounting Dimension", name)
|
||||
row = next((d for d in dimension.dimension_defaults if d.company == self.company), None)
|
||||
|
||||
if row:
|
||||
previous = (row.mandatory_for_pl, row.mandatory_for_bs)
|
||||
self.addCleanup(self.restore_dimension_default, name, previous)
|
||||
else:
|
||||
if not row:
|
||||
row = dimension.append(
|
||||
"dimension_defaults",
|
||||
{"company": self.company, "reference_document": dimension.document_type},
|
||||
)
|
||||
self.addCleanup(self.remove_dimension_default, name)
|
||||
|
||||
row.mandatory_for_pl = mandatory_for_pl
|
||||
row.mandatory_for_bs = mandatory_for_bs
|
||||
dimension.save()
|
||||
|
||||
def restore_dimension_default(self, name, previous):
|
||||
dimension = frappe.get_doc("Accounting Dimension", name)
|
||||
for row in dimension.dimension_defaults:
|
||||
if row.company == self.company:
|
||||
row.mandatory_for_pl, row.mandatory_for_bs = previous
|
||||
dimension.save()
|
||||
|
||||
def remove_dimension_default(self, name):
|
||||
dimension = frappe.get_doc("Accounting Dimension", name)
|
||||
dimension.set(
|
||||
"dimension_defaults",
|
||||
[d for d in dimension.dimension_defaults if d.company != self.company],
|
||||
)
|
||||
dimension.save()
|
||||
|
||||
# tests
|
||||
|
||||
def test_charge_row_dimension_reaches_gl_entry(self):
|
||||
|
||||
@@ -232,7 +232,6 @@ class TestPackedItem(ERPNextTestSuite):
|
||||
|
||||
# a disabled version is rejected
|
||||
frappe.db.set_value("Product Bundle", version, "disabled", 1)
|
||||
self.addCleanup(frappe.db.set_value, "Product Bundle", version, "disabled", 0)
|
||||
self.assertRaises(
|
||||
frappe.ValidationError,
|
||||
get_items_from_product_bundle,
|
||||
|
||||
@@ -29,6 +29,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle
|
||||
)
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.tests.assertions import assert_raises_with_savepoint
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
@@ -1215,9 +1216,6 @@ class TestPurchaseReceipt(ERPNextTestSuite):
|
||||
company="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Location", "Test Location"):
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
cost_center=cost_center,
|
||||
company="_Test Company with perpetual inventory",
|
||||
@@ -1240,9 +1238,6 @@ class TestPurchaseReceipt(ERPNextTestSuite):
|
||||
pr.cancel()
|
||||
|
||||
def test_purchase_receipt_cost_center_with_balance_sheet_account(self):
|
||||
if not frappe.db.exists("Location", "Test Location"):
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
company="_Test Company with perpetual inventory",
|
||||
warehouse="Stores - TCP1",
|
||||
@@ -5684,8 +5679,7 @@ class TestPurchaseReceipt(ERPNextTestSuite):
|
||||
item_code = create_item("Test Item for PR against Rejected Qty").name
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
|
||||
company = frappe.db.get_value("Warehouse", warehouse, "company")
|
||||
rejected_wh = create_warehouse("_Test Rejected Warehouse", company=company)
|
||||
rejected_wh = "_Test Rejected Warehouse - _TC"
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
item_code=item_code,
|
||||
@@ -6537,11 +6531,9 @@ class TestPurchaseReceipt(ERPNextTestSuite):
|
||||
sle_before = frappe.db.count("Stock Ledger Entry", {"voucher_no": pr.name})
|
||||
gle_before = frappe.db.count("GL Entry", {"voucher_no": pr.name})
|
||||
|
||||
frappe.db.savepoint("before_blocked_cancel")
|
||||
with self.assertRaises(frappe.LinkExistsError) as cm:
|
||||
with assert_raises_with_savepoint(self, frappe.LinkExistsError) as cm:
|
||||
pr.cancel()
|
||||
self.assertIn(pi.name, str(cm.exception))
|
||||
frappe.db.rollback(save_point="before_blocked_cancel") # mimic the request-level rollback
|
||||
|
||||
pr.reload()
|
||||
self.assertEqual(pr.docstatus, 1)
|
||||
@@ -6573,23 +6565,8 @@ def create_asset_category_for_pr_test():
|
||||
|
||||
|
||||
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
|
||||
|
||||
company = "_Test Company with perpetual inventory"
|
||||
|
||||
create_internal_customer(
|
||||
"_Test Internal Customer 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
|
||||
create_internal_supplier(
|
||||
"_Test Internal Supplier 2",
|
||||
company,
|
||||
company,
|
||||
)
|
||||
|
||||
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):
|
||||
@@ -6720,9 +6697,6 @@ def get_items(**args):
|
||||
|
||||
|
||||
def make_purchase_receipt(**args):
|
||||
if not frappe.db.exists("Location", "Test Location"):
|
||||
frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
|
||||
|
||||
frappe.db.set_single_value("Buying Settings", "allow_multiple_items", 1)
|
||||
pr = frappe.new_doc("Purchase Receipt")
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -528,8 +528,6 @@ class TestQualityInspection(ERPNextTestSuite):
|
||||
"""Submitting a QI with reference_type 'Job Card' writes its name onto the
|
||||
Job Card's quality_inspection field (the Job Card branch of
|
||||
QualityInspection.update_qc_reference)."""
|
||||
create_item("_Test Item")
|
||||
|
||||
# Job Card whose production_item matches the QI item_code -> must be updated.
|
||||
matching_jc = make_minimal_job_card(production_item="_Test Item")
|
||||
# Job Card with a different production_item -> the production_item filter must
|
||||
@@ -554,7 +552,6 @@ class TestQualityInspection(ERPNextTestSuite):
|
||||
def test_qi_job_card_reference_respects_production_item(self):
|
||||
"""A QI referencing a Job Card by name but whose item_code does not match the
|
||||
Job Card's production_item must NOT update that Job Card."""
|
||||
create_item("_Test Item")
|
||||
mismatch_item = create_item("_Test Item Mismatch QC " + frappe.utils.random_string(6)).name
|
||||
|
||||
# Job Card produces a different item than the QI's item_code.
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import MagicMock, call, patch
|
||||
import frappe
|
||||
from frappe.utils import add_days, add_to_date, now, nowdate, today
|
||||
|
||||
from erpnext.accounts import utils as accounts_utils
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.utils import repost_gle_for_stock_vouchers
|
||||
from erpnext.controllers.stock_controller import create_item_wise_repost_entries
|
||||
@@ -248,19 +249,13 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
se.submit()
|
||||
return se
|
||||
|
||||
@patch.dict(frappe.flags, {"dont_execute_stock_reposts": True})
|
||||
def test_backdated_manufacture_repost_skips_redundant_dependent(self):
|
||||
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import (
|
||||
execute_reposting_entry,
|
||||
)
|
||||
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts", None)
|
||||
|
||||
original_setting = frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")
|
||||
frappe.db.set_single_value("Stock Reposting Settings", "item_based_reposting", 1)
|
||||
self.addCleanup(
|
||||
frappe.db.set_single_value, "Stock Reposting Settings", "item_based_reposting", original_setting
|
||||
)
|
||||
|
||||
company = "_Test Company with perpetual inventory"
|
||||
source_wh = "Stores - TCP1"
|
||||
@@ -337,10 +332,8 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
riv.set_status("Skipped")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
||||
@patch.dict(frappe.flags, {"dont_execute_stock_reposts": True})
|
||||
def test_prevention_of_cancelled_transaction_riv(self):
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts")
|
||||
|
||||
item = make_item()
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
old = make_stock_entry(item_code=item.name, to_warehouse=warehouse, qty=2, rate=5)
|
||||
@@ -374,15 +367,13 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
|
||||
from erpnext.stock.doctype.repost_item_valuation import repost_item_valuation as riv
|
||||
|
||||
orig_max_writes = frappe.db.MAX_WRITES_PER_TRANSACTION
|
||||
self.addCleanup(setattr, frappe.db, "MAX_WRITES_PER_TRANSACTION", orig_max_writes)
|
||||
|
||||
def status_after(error):
|
||||
doc = frappe.new_doc("Repost Item Valuation")
|
||||
doc.name = "test-recoverable-riv"
|
||||
doc.set_status = doc.log_error = doc.db_set = MagicMock()
|
||||
captured = {}
|
||||
with (
|
||||
patch.object(frappe.db, "MAX_WRITES_PER_TRANSACTION", frappe.db.MAX_WRITES_PER_TRANSACTION),
|
||||
patch.object(frappe, "in_test", False),
|
||||
patch.object(frappe.db, "exists", return_value=True),
|
||||
patch.object(frappe.db, "commit"),
|
||||
@@ -397,14 +388,8 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
self.assertEqual(status_after(QueryDeadlockError("deadlock detected")), "In Progress")
|
||||
self.assertEqual(status_after(ValueError("boom")), "Failed")
|
||||
|
||||
@patch.object(accounts_utils, "GL_REPOSTING_CHUNK", 1)
|
||||
def test_gl_repost_progress(self):
|
||||
from erpnext.accounts import utils
|
||||
|
||||
# lower numbers to simplify test
|
||||
orig_chunk_size = utils.GL_REPOSTING_CHUNK
|
||||
utils.GL_REPOSTING_CHUNK = 1
|
||||
self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
|
||||
|
||||
doc = frappe.new_doc("Repost Item Valuation")
|
||||
doc.db_set = MagicMock()
|
||||
|
||||
@@ -425,14 +410,8 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
|
||||
self.assertNotIn(call("gl_reposting_index", 1), doc.db_set.mock_calls)
|
||||
|
||||
@patch.object(accounts_utils, "GL_REPOSTING_CHUNK", 2)
|
||||
def test_gl_complete_gl_reposting(self):
|
||||
from erpnext.accounts import utils
|
||||
|
||||
# lower numbers to simplify test
|
||||
orig_chunk_size = utils.GL_REPOSTING_CHUNK
|
||||
utils.GL_REPOSTING_CHUNK = 2
|
||||
self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
|
||||
|
||||
item = self.make_item().name
|
||||
|
||||
company = "_Test Company with perpetual inventory"
|
||||
@@ -471,14 +450,8 @@ class TestRepostItemValuation(ERPNextTestSuite, StockTestMixin):
|
||||
gle_filters={"account": "Stock In Hand - TCP1"},
|
||||
)
|
||||
|
||||
@patch.object(accounts_utils, "GL_REPOSTING_CHUNK", 2)
|
||||
def test_duplicate_ple_on_repost(self):
|
||||
from erpnext.accounts import utils
|
||||
|
||||
# lower numbers to simplify test
|
||||
orig_chunk_size = utils.GL_REPOSTING_CHUNK
|
||||
utils.GL_REPOSTING_CHUNK = 2
|
||||
self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size)
|
||||
|
||||
rate = 100
|
||||
item = self.make_item()
|
||||
item.valuation_rate = 90
|
||||
|
||||
@@ -1465,9 +1465,7 @@ class TestSerialandBatchBundle(ERPNextTestSuite):
|
||||
|
||||
def _allow_negative_stock_temporarily(self):
|
||||
for field in ("allow_negative_stock", "allow_negative_stock_for_batch"):
|
||||
original = frappe.db.get_single_value("Stock Settings", field)
|
||||
frappe.db.set_single_value("Stock Settings", field, 1)
|
||||
self.addCleanup(frappe.db.set_single_value, "Stock Settings", field, original)
|
||||
|
||||
def _disable_negative_stock(self):
|
||||
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# See license.txt
|
||||
|
||||
import json
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
import frappe
|
||||
@@ -1129,12 +1129,10 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
# original amount
|
||||
self.assertEqual(50, _get_stock_credit(final_consumption))
|
||||
|
||||
@patch.dict(frappe.flags, {"dont_execute_stock_reposts": True})
|
||||
def test_tie_breaking(self):
|
||||
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_entries
|
||||
|
||||
frappe.flags.dont_execute_stock_reposts = True
|
||||
self.addCleanup(frappe.flags.pop, "dont_execute_stock_reposts")
|
||||
|
||||
item = make_item().name
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
|
||||
@@ -1237,8 +1235,6 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
posting_time="02:00:00",
|
||||
)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
reciept2 = make_stock_entry(
|
||||
item_code=item,
|
||||
to_warehouse=warehouse,
|
||||
@@ -1277,8 +1273,6 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
posting_time="02:00:00",
|
||||
)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
# backdated entry with same timestamp but different ms part
|
||||
reciept2 = make_stock_entry(
|
||||
item_code=item,
|
||||
@@ -1323,7 +1317,6 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
posting_date="2021-01-01",
|
||||
posting_time="02:00:00",
|
||||
)
|
||||
time.sleep(1)
|
||||
receipt2 = make_purchase_receipt(
|
||||
item_code=item,
|
||||
warehouse=warehouse,
|
||||
@@ -1380,7 +1373,7 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
)
|
||||
|
||||
dns = []
|
||||
for i in range(5):
|
||||
for i in range(3):
|
||||
dns.append(
|
||||
create_delivery_note(
|
||||
item_code=item,
|
||||
@@ -1391,19 +1384,17 @@ class TestStockLedgerEntry(ERPNextTestSuite, StockTestMixin):
|
||||
posting_time=posting_time,
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
dn = dns[2]
|
||||
dn = dns[1]
|
||||
dn.cancel()
|
||||
|
||||
expected_qty_after_transaction_of_dns3 = 40
|
||||
qty_after_transaction_of_dns3 = frappe.db.get_value(
|
||||
expected_qty_after_transaction = 60
|
||||
qty_after_transaction = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{"voucher_no": dns[3].name, "is_cancelled": 0},
|
||||
{"voucher_no": dns[2].name, "is_cancelled": 0},
|
||||
"qty_after_transaction",
|
||||
)
|
||||
|
||||
self.assertEqual(expected_qty_after_transaction_of_dns3, qty_after_transaction_of_dns3)
|
||||
self.assertEqual(expected_qty_after_transaction, qty_after_transaction)
|
||||
|
||||
def test_get_next_stock_reco_respects_creation_order(self):
|
||||
# A stock reco sharing the exact posting timestamp of the current entry must only count as the
|
||||
|
||||
@@ -17,10 +17,6 @@ TEST_WAREHOUSE = "_Test Warehouse - _TC"
|
||||
|
||||
|
||||
class TestStockRepostingSettings(ERPNextTestSuite):
|
||||
def tearDown(self):
|
||||
frappe.db.set_single_value("Stock Reposting Settings", "repost_incorrect_valuation_entries", 0)
|
||||
super().tearDown()
|
||||
|
||||
def test_auto_repost_disabled_does_nothing(self):
|
||||
frappe.db.set_single_value("Stock Reposting Settings", "repost_incorrect_valuation_entries", 0)
|
||||
with patch("frappe.enqueue") as enqueue:
|
||||
|
||||
@@ -24,8 +24,16 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
class TestStockReservationEntry(ERPNextTestSuite):
|
||||
def setUp(self) -> None:
|
||||
self.warehouse = "_Test Warehouse - _TC"
|
||||
self.sr_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
|
||||
create_material_receipt(items={self.sr_item.name: self.sr_item}, warehouse=self.warehouse, qty=100)
|
||||
self._sr_item = None
|
||||
|
||||
@property
|
||||
def sr_item(self):
|
||||
if self._sr_item is None:
|
||||
self._sr_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
|
||||
create_material_receipt(
|
||||
items={self._sr_item.name: self._sr_item}, warehouse=self.warehouse, qty=100
|
||||
)
|
||||
return self._sr_item
|
||||
|
||||
@ERPNextTestSuite.change_settings("Stock Settings", {"allow_negative_stock": 0})
|
||||
def test_validate_stock_reservation_settings(self) -> None:
|
||||
@@ -192,9 +200,9 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": self.warehouse,
|
||||
"qty": randint(11, 100),
|
||||
"qty": 20,
|
||||
"uom": properties.stock_uom,
|
||||
"rate": randint(10, 400),
|
||||
"rate": 100,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -225,7 +233,7 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
se.cancel()
|
||||
|
||||
# Test - 3: Stock should be fully Reserved if the Available Qty to Reserve is greater than the Un-reserved Qty.
|
||||
create_material_receipt(items_details, self.warehouse, qty=110)
|
||||
create_material_receipt(items_details, self.warehouse, qty=25)
|
||||
so.create_stock_reservation_entries()
|
||||
so.load_from_db()
|
||||
|
||||
@@ -262,9 +270,6 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
do_not_submit=True,
|
||||
)
|
||||
|
||||
for row in so.items:
|
||||
row.qty = 80
|
||||
|
||||
so.save()
|
||||
so.submit()
|
||||
so.create_stock_reservation_entries()
|
||||
@@ -296,7 +301,7 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
dn2 = make_delivery_note(so.name)
|
||||
|
||||
for item in dn2.items:
|
||||
item.qty = 70
|
||||
item.qty = 15
|
||||
|
||||
dn2.save()
|
||||
dn2.submit()
|
||||
@@ -608,7 +613,7 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
)
|
||||
def test_auto_reserve_serial_and_batch(self) -> None:
|
||||
items_details = create_items()
|
||||
create_material_receipt(items_details, self.warehouse, qty=100)
|
||||
create_material_receipt(items_details, self.warehouse, qty=2)
|
||||
|
||||
item_list = []
|
||||
for item_code, properties in items_details.items():
|
||||
@@ -616,9 +621,9 @@ class TestStockReservationEntry(ERPNextTestSuite):
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": self.warehouse,
|
||||
"qty": randint(11, 100),
|
||||
"qty": 2,
|
||||
"uom": properties.stock_uom,
|
||||
"rate": randint(10, 400),
|
||||
"rate": 100,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -8,15 +8,6 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestAvailableBatchReport(ERPNextTestSuite):
|
||||
@staticmethod
|
||||
def _cancel_and_delete_stock_entry(name):
|
||||
if not frappe.db.exists("Stock Entry", name):
|
||||
return
|
||||
doc = frappe.get_doc("Stock Entry", name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc("Stock Entry", name, force=1)
|
||||
|
||||
def test_report_runs_and_lists_batch_qty(self):
|
||||
# The report selects Batch columns (expiry_date, and item_name when show_item_name is set)
|
||||
# while grouping by SLE columns; the Batch PK must be in the GROUP BY for the report to run
|
||||
@@ -35,9 +26,6 @@ class TestAvailableBatchReport(ERPNextTestSuite):
|
||||
se = make_stock_entry(
|
||||
item_code=item, target="_Test Warehouse - _TC", qty=7, basic_rate=10, purpose="Material Receipt"
|
||||
)
|
||||
# make_item is idempotent (returns the existing item), but each receipt stacks a new batch,
|
||||
# so cancel+delete the stock entry to keep repeated runs clean.
|
||||
self.addCleanup(self._cancel_and_delete_stock_entry, se.name)
|
||||
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
filters = frappe._dict(to_date=today(), item_code=item, show_item_name=1)
|
||||
|
||||
@@ -12,21 +12,11 @@ class TestSerialAndBatchSummary(ERPNextTestSuite):
|
||||
|
||||
return execute(frappe._dict(extra))[1]
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete_stock_entry(name):
|
||||
if not frappe.db.exists("Stock Entry", name):
|
||||
return
|
||||
doc = frappe.get_doc("Stock Entry", name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc("Stock Entry", name, force=1)
|
||||
|
||||
def test_serial_receipt_listed(self):
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
item = "_Test Serialized Item With Series"
|
||||
se = make_stock_entry(item_code=item, to_warehouse="Stores - _TC", qty=3, basic_rate=100)
|
||||
self.addCleanup(self._cancel_and_delete_stock_entry, se.name)
|
||||
|
||||
data = self.run_report(voucher_no=[se.name], voucher_type="Stock Entry")
|
||||
|
||||
@@ -55,7 +45,6 @@ class TestSerialAndBatchSummary(ERPNextTestSuite):
|
||||
}
|
||||
).name
|
||||
se = make_stock_entry(item_code=item, to_warehouse="_Test Warehouse - _TC", qty=10, basic_rate=50)
|
||||
self.addCleanup(self._cancel_and_delete_stock_entry, se.name)
|
||||
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
data = self.run_report(voucher_no=[se.name], voucher_type="Stock Entry")
|
||||
|
||||
@@ -230,11 +230,10 @@ class TestStockBalance(ERPNextTestSuite):
|
||||
|
||||
def test_alt_uom_balance_uses_first_alternate_uom(self):
|
||||
"""When an item has multiple alt UOMs, only the first (lowest idx) is shown."""
|
||||
frappe.get_doc({"doctype": "UOM", "uom_name": "Carton"}).insert(ignore_if_duplicate=True)
|
||||
item = self.make_alt_uom_item(
|
||||
uoms=[
|
||||
{"conversion_factor": 12, "uom": "Box"},
|
||||
{"conversion_factor": 144, "uom": "Carton"},
|
||||
{"conversion_factor": 144, "uom": "_Test UOM 1"},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ class TestStockLedgerConversions(ERPNextTestSuite):
|
||||
|
||||
item = make_item("_Test SL Cancel Item", {"is_stock_item": 1}).name
|
||||
se = make_stock_entry(item_code=item, target="_Test Warehouse - _TC", qty=5, basic_rate=100)
|
||||
# register cleanup before the assertions so the entry is removed even if one fails
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
|
||||
self.assertTrue(frappe.db.exists("Stock Ledger Entry", {"voucher_no": se.name, "is_cancelled": 0}))
|
||||
|
||||
@@ -35,8 +33,7 @@ class TestStockLedgerConversions(ERPNextTestSuite):
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
|
||||
item = make_item("_Test SL Valuation Item", {"is_stock_item": 1}).name
|
||||
se = make_stock_entry(item_code=item, target="_Test Warehouse - _TC", qty=10, basic_rate=250)
|
||||
self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name)
|
||||
make_stock_entry(item_code=item, target="_Test Warehouse - _TC", qty=10, basic_rate=250)
|
||||
|
||||
rate = get_valuation_rate(item, "_Test Warehouse - _TC", "Stock Entry", "_TEST-NO-SUCH-VOUCHER")
|
||||
self.assertEqual(rate, 250)
|
||||
@@ -55,12 +52,3 @@ class TestStockLedgerConversions(ERPNextTestSuite):
|
||||
"posting_datetime": now_datetime(),
|
||||
}
|
||||
self.assertIsInstance(get_future_sle_with_negative_qty(args), list | tuple)
|
||||
|
||||
@staticmethod
|
||||
def _cancel_and_delete(doctype, name):
|
||||
if not frappe.db.exists(doctype, name):
|
||||
return
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
frappe.delete_doc(doctype, name, force=1)
|
||||
|
||||
@@ -28,7 +28,6 @@ class TestIssue(TestSetUp):
|
||||
creation = get_datetime("2019-03-04 12:00")
|
||||
|
||||
# make issue with customer specific SLA
|
||||
create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory")
|
||||
issue = make_issue(creation, "_Test Customer", 1)
|
||||
|
||||
self.assertEqual(issue.response_by, get_datetime("2019-03-04 14:00"))
|
||||
|
||||
@@ -519,20 +519,7 @@ def create_service_level_agreement(
|
||||
|
||||
|
||||
def create_customer():
|
||||
customer = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Customer",
|
||||
"customer_name": "_Test Customer",
|
||||
"customer_group": "Commercial",
|
||||
"customer_type": "Individual",
|
||||
"territory": "Rest Of The World",
|
||||
}
|
||||
)
|
||||
if not frappe.db.exists("Customer", "_Test Customer"):
|
||||
customer.insert(ignore_permissions=True)
|
||||
return customer.name
|
||||
else:
|
||||
return frappe.db.exists("Customer", "_Test Customer")
|
||||
return "_Test Customer"
|
||||
|
||||
|
||||
def create_customer_group():
|
||||
|
||||
@@ -8,27 +8,16 @@ from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestPartnersPage(ERPNextTestSuite):
|
||||
def _make_partner(self, name, show_in_website):
|
||||
if not frappe.db.exists("Sales Partner", name):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Sales Partner",
|
||||
"partner_name": name,
|
||||
"territory": "_Test Territory",
|
||||
"commission_rate": 5,
|
||||
"show_in_website": show_in_website,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
return name
|
||||
|
||||
def test_get_context_lists_only_website_partners(self):
|
||||
"""partners.py builds the /partners list via
|
||||
frappe.get_all("Sales Partner", filters={"show_in_website": 1}, ...).
|
||||
Seed one website-visible partner and one hidden control partner, then assert the
|
||||
returned context contains the visible one and excludes the hidden one -- real
|
||||
membership of the converted query's result, not a tautology."""
|
||||
visible = self._make_partner("_Test Website Sales Partner", 1)
|
||||
hidden = self._make_partner("_Test Hidden Sales Partner", 0)
|
||||
visible = "_Test Sales Partner India - 1"
|
||||
hidden = "_Test Sales Partner India - 2"
|
||||
frappe.db.set_value("Sales Partner", visible, "show_in_website", 1)
|
||||
frappe.db.set_value("Sales Partner", hidden, "show_in_website", 0)
|
||||
|
||||
result = get_context(frappe._dict())
|
||||
|
||||
|
||||
24
erpnext/tests/assertions.py
Normal file
24
erpnext/tests/assertions.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
from contextlib import contextmanager
|
||||
from types import SimpleNamespace
|
||||
|
||||
from frappe.database import savepoint
|
||||
|
||||
|
||||
@contextmanager
|
||||
def assert_raises_with_savepoint(test_case, expected_exception):
|
||||
"""Assert an exception while keeping the surrounding test transaction usable."""
|
||||
context = SimpleNamespace(exception=None)
|
||||
with savepoint():
|
||||
try:
|
||||
yield context
|
||||
except Exception as exception:
|
||||
context.exception = exception
|
||||
raise
|
||||
|
||||
if context.exception is None:
|
||||
test_case.fail(f"{expected_exception.__name__} not raised")
|
||||
if not isinstance(context.exception, expected_exception):
|
||||
raise context.exception
|
||||
@@ -1,3 +1,4 @@
|
||||
# This file is solely to trigger BootStrapTestData from CI
|
||||
# utils.py module import instantiates BootStrapTestData
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
# This file is solely to bootstrap shared test data from CI.
|
||||
from erpnext.tests.utils import bootstrap_test_data
|
||||
|
||||
bootstrap_test_data()
|
||||
|
||||
88
erpnext/tests/test_utils.py
Normal file
88
erpnext/tests/test_utils.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.tests.utils import (
|
||||
BootstrapTestData,
|
||||
ERPNextTestSuite,
|
||||
change_settings,
|
||||
if_lending_app_installed,
|
||||
if_lending_app_not_installed,
|
||||
)
|
||||
|
||||
|
||||
class TestERPNextTestUtils(ERPNextTestSuite):
|
||||
def test_make_records_reuses_item_price_when_rate_changes(self):
|
||||
fixture = BootstrapTestData.__new__(BootstrapTestData)
|
||||
filters = {"item_code": "_Test Item", "price_list": "_Test Price List Rest of the World"}
|
||||
item_price = frappe.db.get_value("Item Price", filters, "name")
|
||||
self.assertIsNotNone(item_price)
|
||||
frappe.db.set_value("Item Price", item_price, "price_list_rate", 999)
|
||||
|
||||
fixture.make_item_price()
|
||||
|
||||
self.assertEqual(frappe.db.count("Item Price", filters), 1)
|
||||
self.assertEqual(frappe.db.get_value("Item Price", filters, "price_list_rate"), 10)
|
||||
|
||||
def test_make_custom_doctype_repairs_each_missing_doctype(self):
|
||||
fixture = BootstrapTestData.__new__(BootstrapTestData)
|
||||
existing_doctypes = {"Shelf", "Rack", "Pallet", "Inv Site"}
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
frappe.db,
|
||||
"exists",
|
||||
side_effect=lambda doctype, name: doctype == "DocType" and name in existing_doctypes,
|
||||
),
|
||||
patch("erpnext.tests.utils.frappe.get_doc") as get_doc,
|
||||
):
|
||||
fixture.make_custom_doctype()
|
||||
|
||||
created_doctypes = [call.args[0]["name"] for call in get_doc.call_args_list]
|
||||
self.assertCountEqual(created_doctypes, ["Store", "Order Assignment"])
|
||||
|
||||
def test_change_settings_restores_values_after_error(self):
|
||||
original = frappe.db.get_single_value("Stock Settings", "auto_indent")
|
||||
changed = 0 if original else 1
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "expected failure"):
|
||||
with change_settings("Stock Settings", auto_indent=changed):
|
||||
self.assertEqual(frappe.db.get_single_value("Stock Settings", "auto_indent"), changed)
|
||||
raise RuntimeError("expected failure")
|
||||
|
||||
self.assertEqual(frappe.db.get_single_value("Stock Settings", "auto_indent"), original)
|
||||
|
||||
def test_lending_decorators_preserve_names_and_skip(self):
|
||||
with patch("erpnext.tests.utils.frappe.get_installed_apps", return_value=[]):
|
||||
|
||||
@if_lending_app_installed
|
||||
def requires_lending():
|
||||
return True
|
||||
|
||||
@if_lending_app_not_installed
|
||||
def excludes_lending():
|
||||
return True
|
||||
|
||||
self.assertEqual(requires_lending.__name__, "requires_lending")
|
||||
self.assertEqual(excludes_lending.__name__, "excludes_lending")
|
||||
with self.assertRaises(unittest.SkipTest):
|
||||
requires_lending()
|
||||
self.assertTrue(excludes_lending())
|
||||
|
||||
with patch("erpnext.tests.utils.frappe.get_installed_apps", return_value=["lending"]):
|
||||
|
||||
@if_lending_app_installed
|
||||
def requires_lending():
|
||||
return True
|
||||
|
||||
@if_lending_app_not_installed
|
||||
def excludes_lending():
|
||||
return True
|
||||
|
||||
self.assertTrue(requires_lending())
|
||||
with self.assertRaises(unittest.SkipTest):
|
||||
excludes_lending()
|
||||
@@ -1,19 +1,20 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import copy
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, NewType
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.core.doctype.report.report import get_report_module_dotted_path
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.tests.utils import load_test_records_for
|
||||
from frappe.utils import now_datetime, today
|
||||
from frappe.utils import compare, now_datetime, today
|
||||
|
||||
ReportFilters = dict[str, Any]
|
||||
ReportName = NewType("ReportName", str)
|
||||
_test_data_bootstrapped = False
|
||||
|
||||
|
||||
def execute_script_report(
|
||||
@@ -59,30 +60,20 @@ def execute_script_report(
|
||||
|
||||
def if_lending_app_installed(function):
|
||||
"""Decorator to check if lending app is installed"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
if "lending" in frappe.get_installed_apps():
|
||||
return function(*args, **kwargs)
|
||||
return
|
||||
|
||||
return wrapper
|
||||
return unittest.skipUnless("lending" in frappe.get_installed_apps(), "lending is not installed")(function)
|
||||
|
||||
|
||||
def if_lending_app_not_installed(function):
|
||||
"""Decorator to check if lending app is not installed"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
if "lending" not in frappe.get_installed_apps():
|
||||
return function(*args, **kwargs)
|
||||
return
|
||||
|
||||
return wrapper
|
||||
return unittest.skipIf("lending" in frappe.get_installed_apps(), "lending is installed")(function)
|
||||
|
||||
|
||||
class BootStrapTestData:
|
||||
class BootstrapTestData:
|
||||
def __init__(self):
|
||||
self.make_presets()
|
||||
self.make_master_data()
|
||||
lock_name = f"{frappe.local.site}:erpnext-test-data"
|
||||
with frappe.db.advisory_lock(lock_name, timeout=300):
|
||||
self.make_presets()
|
||||
self.make_master_data()
|
||||
|
||||
def make_presets(self):
|
||||
from frappe.desk.page.setup_wizard.install_fixtures import update_genders, update_salutations
|
||||
@@ -255,25 +246,56 @@ class BootStrapTestData:
|
||||
stock_settings.enable_serial_and_batch_no_for_item = 1
|
||||
stock_settings.save()
|
||||
|
||||
def make_records(self, key, records):
|
||||
doctype = records[0].get("doctype")
|
||||
def make_records(self, key, records, update_fields=()):
|
||||
"""Create shared fixtures once and repair explicitly mutable values."""
|
||||
if not records:
|
||||
return
|
||||
if not key:
|
||||
raise ValueError("make_records expects at least one identity field")
|
||||
|
||||
def get_filters(record):
|
||||
filters = {}
|
||||
for x in key:
|
||||
filters[x] = record.get(x)
|
||||
return filters
|
||||
doctypes = {record.get("doctype") for record in records}
|
||||
if len(doctypes) != 1 or None in doctypes:
|
||||
raise ValueError("make_records expects records for exactly one DocType")
|
||||
|
||||
for x in records:
|
||||
filters = get_filters(x)
|
||||
if not frappe.db.exists(doctype, filters):
|
||||
frappe.get_doc(x).insert()
|
||||
doctype = doctypes.pop()
|
||||
for record in records:
|
||||
filters = {fieldname: record.get(fieldname) for fieldname in key}
|
||||
if not any(value is not None for value in filters.values()):
|
||||
raise ValueError(f"make_records expects an identity for {doctype}")
|
||||
|
||||
if name := frappe.db.exists(doctype, filters):
|
||||
self._update_fixture_values(doctype, name, record, update_fields)
|
||||
else:
|
||||
frappe.get_doc(record).insert(ignore_if_duplicate=True)
|
||||
|
||||
@staticmethod
|
||||
def _update_fixture_values(doctype, name, record, update_fields):
|
||||
if not update_fields:
|
||||
return
|
||||
|
||||
doc = frappe.get_doc(doctype, name)
|
||||
changed = False
|
||||
for fieldname in update_fields:
|
||||
if fieldname not in record:
|
||||
continue
|
||||
|
||||
expected = record[fieldname]
|
||||
field = doc.meta.get_field(fieldname)
|
||||
fieldtype = field.fieldtype if field else None
|
||||
if compare(doc.get(fieldname), "=", expected, fieldtype):
|
||||
continue
|
||||
|
||||
doc.set(fieldname, expected)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
def make_price_list(self):
|
||||
records = [
|
||||
{
|
||||
"doctype": "Price List",
|
||||
"price_list_name": _("Standard Buying"),
|
||||
"price_list_name": "Standard Buying",
|
||||
"enabled": 1,
|
||||
"buying": 1,
|
||||
"selling": 0,
|
||||
@@ -281,7 +303,7 @@ class BootStrapTestData:
|
||||
},
|
||||
{
|
||||
"doctype": "Price List",
|
||||
"price_list_name": _("Standard Selling"),
|
||||
"price_list_name": "Standard Selling",
|
||||
"enabled": 1,
|
||||
"buying": 0,
|
||||
"selling": 1,
|
||||
@@ -337,7 +359,11 @@ class BootStrapTestData:
|
||||
"selling": 0,
|
||||
},
|
||||
]
|
||||
self.make_records(["price_list_name", "enabled", "selling", "buying", "currency"], records)
|
||||
self.make_records(
|
||||
["price_list_name"],
|
||||
records,
|
||||
update_fields=("enabled", "selling", "buying", "currency", "price_not_uom_dependant"),
|
||||
)
|
||||
|
||||
def make_monthly_distribution(self):
|
||||
records = [
|
||||
@@ -441,7 +467,7 @@ class BootStrapTestData:
|
||||
"parent_department": "All Departments",
|
||||
},
|
||||
]
|
||||
self.make_records(["department_name"], records)
|
||||
self.make_records(["department_name", "company"], records)
|
||||
|
||||
def make_role(self):
|
||||
records = [
|
||||
@@ -590,7 +616,7 @@ class BootStrapTestData:
|
||||
"user_id": "test2@example.com",
|
||||
},
|
||||
]
|
||||
self.make_records(["first_name"], records)
|
||||
self.make_records(["user_id"], records)
|
||||
|
||||
def make_sales_person(self):
|
||||
records = [
|
||||
@@ -726,8 +752,11 @@ class BootStrapTestData:
|
||||
}
|
||||
)
|
||||
|
||||
key = ["year_start_date", "year_end_date"]
|
||||
self.make_records(key, records)
|
||||
self.make_records(
|
||||
["year"],
|
||||
records,
|
||||
update_fields=("year_start_date", "year_end_date", "is_short_year"),
|
||||
)
|
||||
|
||||
def make_payment_term(self):
|
||||
records = [
|
||||
@@ -1915,7 +1944,7 @@ class BootStrapTestData:
|
||||
"company": "_Test Company",
|
||||
},
|
||||
]
|
||||
self.make_records(["item_code", "item_name"], records)
|
||||
self.make_records(["item_code"], records, update_fields=("item_name",))
|
||||
|
||||
def make_product_bundle(self):
|
||||
from erpnext.selling.doctype.product_bundle.product_bundle import get_active_product_bundle
|
||||
@@ -2544,7 +2573,7 @@ class BootStrapTestData:
|
||||
},
|
||||
{
|
||||
"doctype": "Item Price",
|
||||
"price_list": _("Standard Selling"),
|
||||
"price_list": "Standard Selling",
|
||||
"item_code": "Loyal Item",
|
||||
"price_list_rate": 10000,
|
||||
},
|
||||
@@ -2555,7 +2584,11 @@ class BootStrapTestData:
|
||||
"price_list_rate": 10000,
|
||||
},
|
||||
]
|
||||
self.make_records(["item_code", "price_list", "price_list_rate"], records)
|
||||
self.make_records(
|
||||
["item_code", "price_list", "customer", "supplier"],
|
||||
records,
|
||||
update_fields=("price_list_rate", "valid_from", "valid_upto", "uom", "packing_unit", "batch_no"),
|
||||
)
|
||||
|
||||
def make_currency_exchange(self):
|
||||
"""Seed current-dated USD<->INR rates so foreign-currency documents
|
||||
@@ -2587,7 +2620,16 @@ class BootStrapTestData:
|
||||
"for_selling": 1,
|
||||
},
|
||||
]
|
||||
self.make_records(["from_currency", "to_currency", "date", "for_buying", "for_selling"], records)
|
||||
identity_fields = ("from_currency", "to_currency", "for_buying", "for_selling")
|
||||
for record in records:
|
||||
filters = {fieldname: record.get(fieldname) for fieldname in identity_fields}
|
||||
name = frappe.db.get_value("Currency Exchange", filters, "name", order_by="date desc")
|
||||
if name:
|
||||
self._update_fixture_values(
|
||||
"Currency Exchange", name, record, update_fields=("date", "exchange_rate")
|
||||
)
|
||||
else:
|
||||
frappe.get_doc(record).insert(ignore_if_duplicate=True)
|
||||
|
||||
def make_operation(self):
|
||||
records = [
|
||||
@@ -2713,160 +2755,87 @@ class BootStrapTestData:
|
||||
self.make_records(["finance_book_name"], records)
|
||||
|
||||
def make_custom_doctype(self):
|
||||
if not frappe.db.exists("DocType", "Shelf"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Shelf",
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": "field:shelf_name",
|
||||
"fields": [{"label": "Shelf Name", "fieldname": "shelf_name", "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
for doctype, fieldname, label in (
|
||||
("Shelf", "shelf_name", "Shelf Name"),
|
||||
("Rack", "rack_name", "Rack Name"),
|
||||
("Pallet", "pallet_name", "Pallet Name"),
|
||||
("Inv Site", "site_name", "Site Name"),
|
||||
("Store", "store_name", "Store Name"),
|
||||
):
|
||||
self._make_simple_custom_doctype(doctype, fieldname, label)
|
||||
|
||||
if not frappe.db.exists("DocType", "Rack"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Rack",
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": "field:rack_name",
|
||||
"fields": [{"label": "Rack Name", "fieldname": "rack_name", "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
self._make_order_assignment_doctype()
|
||||
|
||||
if not frappe.db.exists("DocType", "Pallet"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Pallet",
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": "field:pallet_name",
|
||||
"fields": [{"label": "Pallet Name", "fieldname": "pallet_name", "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
@staticmethod
|
||||
def _make_simple_custom_doctype(doctype, fieldname, label):
|
||||
if frappe.db.exists("DocType", doctype):
|
||||
return
|
||||
|
||||
if not frappe.db.exists("DocType", "Inv Site"):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Inv Site",
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": "field:site_name",
|
||||
"fields": [{"label": "Site Name", "fieldname": "site_name", "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
if not frappe.db.exists("DocType", "Store"):
|
||||
frappe.get_doc(
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": doctype,
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": f"field:{fieldname}",
|
||||
"fields": [{"label": label, "fieldname": fieldname, "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Store",
|
||||
"module": "Stock",
|
||||
"custom": 1,
|
||||
"naming_rule": "By fieldname",
|
||||
"autoname": "field:store_name",
|
||||
"fields": [{"label": "Store Name", "fieldname": "store_name", "fieldtype": "Data"}],
|
||||
"permissions": [
|
||||
{
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
],
|
||||
"role": "System Manager",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
||||
if not frappe.db.exists("DocType", "Order Assignment"):
|
||||
frappe.get_doc(
|
||||
@staticmethod
|
||||
def _make_order_assignment_doctype():
|
||||
if frappe.db.exists("DocType", "Order Assignment"):
|
||||
return
|
||||
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Order Assignment",
|
||||
"module": "Buying",
|
||||
"custom": 1,
|
||||
"autoname": "field:po",
|
||||
"fields": [
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Order Assignment",
|
||||
"module": "Buying",
|
||||
"custom": 1,
|
||||
"autoname": "field:po",
|
||||
"fields": [
|
||||
{
|
||||
"label": "PO",
|
||||
"fieldname": "po",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Order",
|
||||
},
|
||||
{
|
||||
"label": "Supplier",
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Data",
|
||||
"fetch_from": "po.supplier",
|
||||
},
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
{"read": 1, "role": "Supplier"},
|
||||
],
|
||||
}
|
||||
).insert(ignore_if_duplicate=True)
|
||||
"label": "PO",
|
||||
"fieldname": "po",
|
||||
"fieldtype": "Link",
|
||||
"options": "Purchase Order",
|
||||
},
|
||||
{
|
||||
"label": "Supplier",
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Data",
|
||||
"fetch_from": "po.supplier",
|
||||
},
|
||||
],
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1,
|
||||
},
|
||||
{"read": 1, "role": "Supplier"},
|
||||
],
|
||||
}
|
||||
).insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
||||
def make_address(self):
|
||||
records = [
|
||||
@@ -3046,7 +3015,21 @@ class BootStrapTestData:
|
||||
self.make_records(["store_name"], records)
|
||||
|
||||
|
||||
BootStrapTestData()
|
||||
# Keep the old spelling for test helpers in downstream apps.
|
||||
BootStrapTestData = BootstrapTestData
|
||||
|
||||
|
||||
def bootstrap_test_data():
|
||||
global _test_data_bootstrapped
|
||||
if _test_data_bootstrapped:
|
||||
return
|
||||
|
||||
BootstrapTestData()
|
||||
_test_data_bootstrapped = True
|
||||
|
||||
|
||||
# Downstream apps create their fixtures while importing this module.
|
||||
bootstrap_test_data()
|
||||
|
||||
|
||||
class ERPNextTestSuite(unittest.TestCase):
|
||||
@@ -3060,6 +3043,7 @@ class ERPNextTestSuite(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
bootstrap_test_data()
|
||||
cls.globalTestRecords = {}
|
||||
|
||||
def tearDown(self):
|
||||
@@ -3086,24 +3070,21 @@ class ERPNextTestSuite(unittest.TestCase):
|
||||
@ERPNextTestSuite.registerAs(staticmethod)
|
||||
@contextmanager
|
||||
def change_settings(doctype, settings_dict=None, /, **settings) -> None:
|
||||
"""Temporarily: change settings in a settings doctype."""
|
||||
import copy
|
||||
|
||||
"""Temporarily change fields in a settings DocType."""
|
||||
if settings_dict is None:
|
||||
settings_dict = settings
|
||||
|
||||
settings = frappe.get_doc(doctype)
|
||||
previous_settings = copy.deepcopy(settings_dict)
|
||||
for key in previous_settings:
|
||||
previous_settings[key] = getattr(settings, key)
|
||||
settings_doc = frappe.get_doc(doctype)
|
||||
previous_settings = {key: copy.deepcopy(settings_doc.get(key)) for key in settings_dict}
|
||||
|
||||
for key, value in settings_dict.items():
|
||||
setattr(settings, key, value)
|
||||
settings.save(ignore_permissions=True)
|
||||
settings_doc.set(key, value)
|
||||
settings_doc.save(ignore_permissions=True)
|
||||
|
||||
yield
|
||||
|
||||
settings = frappe.get_doc(doctype)
|
||||
for key, value in previous_settings.items():
|
||||
setattr(settings, key, value)
|
||||
settings.save(ignore_permissions=True)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
settings_doc = frappe.get_doc(doctype)
|
||||
for key, value in previous_settings.items():
|
||||
settings_doc.set(key, value)
|
||||
settings_doc.save(ignore_permissions=True)
|
||||
|
||||
Reference in New Issue
Block a user