mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
Merge branch 'version-16-hotfix' into mergify/bp/version-16-hotfix/pr-53588
This commit is contained in:
@@ -173,6 +173,7 @@ class Customer(TransactionBase):
|
||||
def validate(self):
|
||||
self.flags.is_new_doc = self.is_new()
|
||||
self.flags.old_lead = self.lead_name
|
||||
self.validate_customer_group()
|
||||
validate_party_accounts(self)
|
||||
self.validate_credit_limit_on_change()
|
||||
self.set_loyalty_program()
|
||||
@@ -356,6 +357,17 @@ class Customer(TransactionBase):
|
||||
frappe.NameError,
|
||||
)
|
||||
|
||||
def validate_customer_group(self):
|
||||
if not self.customer_group:
|
||||
return
|
||||
|
||||
is_group = frappe.db.get_value("Customer Group", self.customer_group, "is_group")
|
||||
if is_group:
|
||||
frappe.throw(
|
||||
_("Cannot select a Group type Customer Group. Please select a non-group Customer Group."),
|
||||
title=_("Invalid Customer Group"),
|
||||
)
|
||||
|
||||
def validate_credit_limit_on_change(self):
|
||||
if self.get("__islocal") or not self.credit_limits:
|
||||
return
|
||||
|
||||
@@ -1029,11 +1029,11 @@ class TestQuotation(ERPNextTestSuite):
|
||||
def test_make_quotation_qar_to_inr(self):
|
||||
quotation = make_quotation(
|
||||
currency="QAR",
|
||||
transaction_date="2026-06-04",
|
||||
transaction_date="2026-01-01",
|
||||
)
|
||||
|
||||
cache = frappe.cache()
|
||||
key = "currency_exchange_rate_{}:{}:{}".format("2026-06-04", "QAR", "INR")
|
||||
key = "currency_exchange_rate_{}:{}:{}".format("2026-01-01", "QAR", "INR")
|
||||
value = cache.get(key)
|
||||
expected_rate = flt(value) / 3.64
|
||||
|
||||
|
||||
@@ -63,6 +63,13 @@ frappe.ui.form.on("Sales Order", {
|
||||
});
|
||||
}
|
||||
},
|
||||
transaction_date(frm) {
|
||||
prevent_past_delivery_dates(frm);
|
||||
frm.set_value("delivery_date", "");
|
||||
frm.doc.items.forEach((d) => {
|
||||
frappe.model.set_value(d.doctype, d.name, "delivery_date", "");
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.fields_dict["items"].grid.update_docfield_property(
|
||||
|
||||
@@ -10,7 +10,6 @@ from frappe.core.doctype.user_permission.test_user_permission import create_user
|
||||
from frappe.tests import change_settings
|
||||
from frappe.utils import add_days, flt, nowdate, today
|
||||
|
||||
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError, get_due_date, update_child_qty_rate
|
||||
from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import (
|
||||
make_maintenance_schedule,
|
||||
@@ -35,10 +34,7 @@ from erpnext.stock.get_item_details import get_bin_details
|
||||
from erpnext.tests.utils import ERPNextTestSuite
|
||||
|
||||
|
||||
class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
def setUp(self):
|
||||
self.create_customer("_Test Customer Credit")
|
||||
|
||||
class TestSalesOrder(ERPNextTestSuite):
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Stock Settings",
|
||||
{
|
||||
@@ -2439,7 +2435,7 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
def test_credit_limit_on_so_reopning(self):
|
||||
# set credit limit
|
||||
company = "_Test Company"
|
||||
customer = frappe.get_doc("Customer", self.customer)
|
||||
customer = frappe.get_doc("Customer", "_Test Customer")
|
||||
customer.credit_limits = []
|
||||
customer.append(
|
||||
"credit_limits", {"company": company, "credit_limit": 1000, "bypass_credit_limit_check": False}
|
||||
@@ -2447,35 +2443,33 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
customer.save()
|
||||
|
||||
so1 = make_sales_order(qty=9, rate=100, do_not_submit=True)
|
||||
so1.customer = self.customer
|
||||
so1.customer = customer.name
|
||||
so1.save().submit()
|
||||
|
||||
so1.update_status("Closed")
|
||||
|
||||
so2 = make_sales_order(qty=9, rate=100, do_not_submit=True)
|
||||
so2.customer = self.customer
|
||||
so2.customer = customer.name
|
||||
so2.save().submit()
|
||||
|
||||
self.assertRaises(frappe.ValidationError, so1.update_status, "Draft")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Stock Settings", {"enable_stock_reservation": True})
|
||||
def test_warehouse_mapping_based_on_stock_reservation(self):
|
||||
self.create_company(company_name="Glass Ceiling", abbr="GC")
|
||||
self.create_item("Lamy Safari 2", True, self.warehouse_stores, self.company, 2000)
|
||||
self.create_customer()
|
||||
self.clear_old_entries()
|
||||
warehouse = "Stores - _TC"
|
||||
warehouse_finished = "Finished Goods - _TC"
|
||||
|
||||
so = frappe.new_doc("Sales Order")
|
||||
so.company = self.company
|
||||
so.customer = self.customer
|
||||
so.company = "_Test Company"
|
||||
so.customer = "_Test Customer"
|
||||
so.transaction_date = today()
|
||||
so.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": self.item,
|
||||
"item_code": "_Test Item",
|
||||
"qty": 10,
|
||||
"rate": 2000,
|
||||
"warehouse": self.warehouse_stores,
|
||||
"warehouse": "Stores - _TC",
|
||||
"delivery_date": today(),
|
||||
},
|
||||
)
|
||||
@@ -2485,12 +2479,12 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
se = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Stock Entry",
|
||||
"company": self.company,
|
||||
"company": "_Test Company",
|
||||
"stock_entry_type": "Material Receipt",
|
||||
"posting_date": today(),
|
||||
"items": [
|
||||
{"item_code": self.item, "t_warehouse": self.warehouse_stores, "qty": 5},
|
||||
{"item_code": self.item, "t_warehouse": self.warehouse_finished_goods, "qty": 5},
|
||||
{"item_code": "_Test Item", "t_warehouse": warehouse, "qty": 5},
|
||||
{"item_code": "_Test Item", "t_warehouse": warehouse_finished, "qty": 5},
|
||||
],
|
||||
}
|
||||
)
|
||||
@@ -2503,7 +2497,7 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
{
|
||||
"sales_order_item": itm.name,
|
||||
"item_code": itm.item_code,
|
||||
"warehouse": self.warehouse_stores,
|
||||
"warehouse": warehouse,
|
||||
"qty_to_reserve": 2,
|
||||
}
|
||||
]
|
||||
@@ -2513,7 +2507,7 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
{
|
||||
"sales_order_item": itm.name,
|
||||
"item_code": itm.item_code,
|
||||
"warehouse": self.warehouse_finished_goods,
|
||||
"warehouse": warehouse_finished,
|
||||
"qty_to_reserve": 3,
|
||||
}
|
||||
]
|
||||
@@ -2523,31 +2517,31 @@ class TestSalesOrder(AccountsTestMixin, ERPNextTestSuite):
|
||||
dn = make_delivery_note(so.name, kwargs={"for_reserved_stock": True})
|
||||
self.assertEqual(2, len(dn.items))
|
||||
self.assertEqual(dn.items[0].qty, 2)
|
||||
self.assertEqual(dn.items[0].warehouse, self.warehouse_stores)
|
||||
self.assertEqual(dn.items[0].warehouse, warehouse)
|
||||
self.assertEqual(dn.items[1].qty, 3)
|
||||
self.assertEqual(dn.items[1].warehouse, self.warehouse_finished_goods)
|
||||
self.assertEqual(dn.items[1].warehouse, warehouse_finished)
|
||||
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
|
||||
warehouse = create_warehouse("Test Warehouse 1", company=self.company)
|
||||
warehouse = create_warehouse("Test Warehouse 1", company="_Test Company")
|
||||
|
||||
make_stock_entry(
|
||||
item_code=self.item,
|
||||
item_code="_Test Item",
|
||||
target=warehouse,
|
||||
qty=5,
|
||||
company=self.company,
|
||||
company="_Test Company",
|
||||
)
|
||||
|
||||
so = frappe.new_doc("Sales Order")
|
||||
so.reserve_stock = 1
|
||||
so.company = self.company
|
||||
so.customer = self.customer
|
||||
so.company = "_Test Company"
|
||||
so.customer = "_Test Customer"
|
||||
so.transaction_date = today()
|
||||
so.currency = "INR"
|
||||
so.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": self.item,
|
||||
"item_code": "_Test Item",
|
||||
"qty": 5,
|
||||
"rate": 2000,
|
||||
"warehouse": warehouse,
|
||||
|
||||
@@ -343,7 +343,8 @@
|
||||
"fieldname": "discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Discount Amount",
|
||||
"options": "currency"
|
||||
"options": "currency",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||
@@ -503,12 +504,14 @@
|
||||
{
|
||||
"fieldname": "weight_per_unit",
|
||||
"fieldtype": "Float",
|
||||
"label": "Weight Per Unit"
|
||||
"label": "Weight Per Unit",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_weight",
|
||||
"fieldtype": "Float",
|
||||
"label": "Total Weight",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -822,6 +825,7 @@
|
||||
"label": "Rate of Stock UOM",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -830,6 +834,7 @@
|
||||
"fieldname": "grant_commission",
|
||||
"fieldtype": "Check",
|
||||
"label": "Grant Commission",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -837,6 +842,7 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Picked Qty (in Stock UOM)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -910,6 +916,7 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Production Plan Qty",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -926,7 +933,8 @@
|
||||
"fieldname": "distributed_discount_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
"options": "currency",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@@ -995,6 +1003,7 @@
|
||||
"label": "Subcontracted Quantity",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -1010,7 +1019,8 @@
|
||||
"fieldname": "fg_item_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Finished Good Qty",
|
||||
"mandatory_depends_on": "eval:parent.is_subcontracted"
|
||||
"mandatory_depends_on": "eval:parent.is_subcontracted",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "requested_qty",
|
||||
@@ -1025,7 +1035,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-02-21 16:39:00.200328",
|
||||
"modified": "2026-02-22 16:40:00.200328",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"section_break_zwh6",
|
||||
"allow_delivery_of_overproduced_qty",
|
||||
"column_break_mla9",
|
||||
"deliver_scrap_items"
|
||||
"deliver_secondary_items"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -260,13 +260,6 @@
|
||||
"fieldname": "column_break_mla9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If enabled, the Scrap Item generated against a Finished Good will also be added in the Stock Entry when delivering that Finished Good.",
|
||||
"fieldname": "deliver_scrap_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Deliver Scrap Items"
|
||||
},
|
||||
{
|
||||
"fieldname": "item_price_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
@@ -320,6 +313,13 @@
|
||||
"fieldname": "enable_utm",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable UTM"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If enabled, the Secondary Items generated against a Finished Good will also be added in the Stock Entry when delivering that Finished Good.",
|
||||
"fieldname": "deliver_secondary_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Deliver Secondary Items"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
|
||||
@@ -41,7 +41,7 @@ class SellingSettings(Document):
|
||||
blanket_order_allowance: DF.Float
|
||||
cust_master_name: DF.Literal["Customer Name", "Naming Series", "Auto Name"]
|
||||
customer_group: DF.Link | None
|
||||
deliver_scrap_items: DF.Check
|
||||
deliver_secondary_items: DF.Check
|
||||
dn_required: DF.Literal["No", "Yes"]
|
||||
dont_reserve_sales_order_qty_on_sales_return: DF.Check
|
||||
editable_bundle_item_rates: DF.Check
|
||||
@@ -93,10 +93,10 @@ class SellingSettings(Document):
|
||||
|
||||
self.validate_fallback_to_default_price_list()
|
||||
|
||||
if old_doc.enable_tracking_sales_commissions != self.enable_tracking_sales_commissions:
|
||||
if old_doc and old_doc.enable_tracking_sales_commissions != self.enable_tracking_sales_commissions:
|
||||
toggle_tracking_sales_commissions_section(not self.enable_tracking_sales_commissions)
|
||||
|
||||
if old_doc.enable_utm != self.enable_utm:
|
||||
if old_doc and old_doc.enable_utm != self.enable_utm:
|
||||
toggle_utm_analytics_section(not self.enable_utm)
|
||||
|
||||
def validate_fallback_to_default_price_list(self):
|
||||
|
||||
Reference in New Issue
Block a user