test: improve ERPNext test isolation and runtime (#58507)

This commit is contained in:
Mihir Kandoi
2026-08-28 10:41:22 +05:30
committed by GitHub
parent 9018573179
commit 9160182727
78 changed files with 779 additions and 1184 deletions

View File

@@ -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()

View File

@@ -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(

View File

@@ -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")

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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)

View File

@@ -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})

View File

@@ -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

View File

@@ -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(

View File

@@ -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",

View File

@@ -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",

View File

@@ -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"

View File

@@ -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",

View File

@@ -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)

View File

@@ -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,

View File

@@ -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",

View File

@@ -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

View File

@@ -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):

View File

@@ -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(

View File

@@ -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",

View File

@@ -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,
)
)

View File

@@ -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")

View File

@@ -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()

View File

@@ -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)

View File

@@ -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(
[

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):

View File

@@ -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(

View File

@@ -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"

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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",

View File

@@ -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)

View File

@@ -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",

View File

@@ -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):

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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}

View File

@@ -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)

View File

@@ -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":

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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"])

View File

@@ -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")

View File

@@ -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()

View File

@@ -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):

View File

@@ -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,

View File

@@ -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 = []

View File

@@ -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):

View File

@@ -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,

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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,
}
)

View File

@@ -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)

View File

@@ -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")

View File

@@ -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"},
]
)

View File

@@ -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)

View File

@@ -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"))

View File

@@ -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():

View File

@@ -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())

View 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

View File

@@ -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()

View 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()

View File

@@ -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)