Merge pull request #3822 from neilLasrado/item-uom
Changed UOM validation for Item Master
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 089c067..eaf904d 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -48,7 +48,7 @@
self.website_image = self.image
self.check_warehouse_is_set_for_stock_item()
- self.check_stock_uom_with_bin()
+ self.validate_uom()
self.add_default_uom_in_conversion_factor_table()
self.validate_conversion_factor()
self.validate_item_type()
@@ -105,35 +105,6 @@
[self.remove(d) for d in to_remove]
-
- def check_stock_uom_with_bin(self):
- if not self.get("__islocal"):
- if self.stock_uom == frappe.db.get_value("Item", self.name, "stock_uom"):
- return
-
- matched=True
- ref_uom = frappe.db.get_value("Stock Ledger Entry",
- {"item_code": self.name}, "stock_uom")
-
- if ref_uom:
- if cstr(ref_uom) != cstr(self.stock_uom):
- matched = False
- else:
- bin_list = frappe.db.sql("select * from tabBin where item_code=%s",
- self.item_code, as_dict=1)
- for bin in bin_list:
- if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
- or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(self.stock_uom):
- matched = False
- break
-
- if matched and bin_list:
- frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""",
- (self.stock_uom, self.name))
-
- if not matched:
- frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module.").format(self.name))
-
def update_template_tables(self):
template = frappe.get_doc("Item", self.variant_of)
@@ -344,6 +315,17 @@
or ifnull(reserved_qty, 0) > 0 or ifnull(indented_qty, 0) > 0 or ifnull(planned_qty, 0) > 0)""", self.name)
if stock_in:
frappe.throw(_("Item Template cannot have stock or Open Sales/Purchase/Production Orders."), ItemTemplateCannotHaveStock)
+
+ def validate_uom(self):
+ if not self.get("__islocal"):
+ check_stock_uom_with_bin(self.name, self.stock_uom)
+ if self.has_variants:
+ for d in frappe.db.get_all("Item", filters= {"variant_of": self.name}):
+ check_stock_uom_with_bin(d.name, self.stock_uom)
+ if self.variant_of:
+ template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
+ if template_uom != self.stock_uom:
+ frappe.throw(_("Default Unit of Measure for Variant must be same as Template"))
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
if not end_of_life:
@@ -449,3 +431,30 @@
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
invalidate_cache_for(doc, doc.old_item_group)
+
+def check_stock_uom_with_bin(item, stock_uom):
+ if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
+ return
+
+ matched=True
+ ref_uom = frappe.db.get_value("Stock Ledger Entry",
+ {"item_code": item}, "stock_uom")
+
+ if ref_uom:
+ if cstr(ref_uom) != cstr(stock_uom):
+ matched = False
+ else:
+ bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
+ for bin in bin_list:
+ if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
+ or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
+ matched = False
+ break
+
+ if matched and bin_list:
+ frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
+
+ if not matched:
+ frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because \
+ you have already made some transaction(s) with another UOM. To change default UOM, \
+ use 'UOM Replace Utility' tool under Stock module.").format(item))
diff --git a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
index c3f530a..5b5419d 100644
--- a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
+++ b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
@@ -10,6 +10,28 @@
from frappe.model.document import Document
class StockUOMReplaceUtility(Document):
+
+ # Update Stock UOM
+ def update_stock_uom(self):
+ self.validate_item()
+ self.validate_mandatory()
+ self.validate_uom_integer_type()
+
+ update_stock_ledger_entry(self.item_code, self.new_stock_uom, self.conversion_factor)
+ update_bin(self.item_code, self.new_stock_uom, self.conversion_factor)
+ update_item_master(self.item_code, self.new_stock_uom, self.conversion_factor)
+
+ #if item is template change UOM for all associated variants
+ if frappe.db.get_value("Item", self.item_code, "has_variants"):
+ for d in frappe.db.get_all("Item", filters= {"variant_of": self.item_code}):
+ update_stock_ledger_entry(d.name, self.new_stock_uom, self.conversion_factor)
+ update_bin(d.name, self.new_stock_uom, self.conversion_factor)
+ update_item_master(d.name, self.new_stock_uom, self.conversion_factor)
+
+ def validate_item(self):
+ if frappe.db.get_value("Item", self.item_code, "variant_of"):
+ frappe.throw(_("You cannot change default UOM of Variant. To change default UOM for Variant change default UOM of the Template"))
+
def validate_mandatory(self):
if not cstr(self.item_code):
frappe.throw(_("Item is required"))
@@ -27,72 +49,7 @@
stock_uom = frappe.db.get_value("Item", self.item_code, "stock_uom")
if cstr(self.new_stock_uom) == cstr(stock_uom):
frappe.throw(_("Item is updated"))
-
- def update_item_master(self):
- item_doc = frappe.get_doc("Item", self.item_code)
- item_doc.stock_uom = self.new_stock_uom
- item_doc.save()
-
- frappe.msgprint(_("Stock UOM updated for Item {0}").format(self.item_code))
-
- def update_bin(self):
- # update bin
- if flt(self.conversion_factor) != flt(1):
- frappe.db.sql("""update `tabBin`
- set stock_uom = %s,
- indented_qty = ifnull(indented_qty,0) * %s,
- ordered_qty = ifnull(ordered_qty,0) * %s,
- reserved_qty = ifnull(reserved_qty,0) * %s,
- planned_qty = ifnull(planned_qty,0) * %s,
- projected_qty = actual_qty + ordered_qty + indented_qty +
- planned_qty - reserved_qty
- where item_code = %s""", (self.new_stock_uom, self.conversion_factor,
- self.conversion_factor, self.conversion_factor,
- self.conversion_factor, self.item_code))
- else:
- frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s",
- (self.new_stock_uom, self.item_code) )
-
- # acknowledge user
- frappe.msgprint(_("Stock balances updated"))
-
- def update_stock_ledger_entry(self):
- # update stock ledger entry
- from erpnext.stock.stock_ledger import update_entries_after
-
- if flt(self.conversion_factor) != flt(1):
- frappe.db.sql("""update `tabStock Ledger Entry`
- set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s
- where item_code = %s""",
- (self.new_stock_uom, self.conversion_factor, self.item_code))
- else:
- frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s
- where item_code=%s""", (self.new_stock_uom, self.item_code))
-
- # acknowledge user
- frappe.msgprint(_("Stock Ledger entries balances updated"))
-
- # update item valuation
- if flt(self.conversion_factor) != flt(1):
- wh = frappe.db.sql("select name from `tabWarehouse`")
- for w in wh:
- update_entries_after({"item_code": self.item_code, "warehouse": w[0]})
-
- # acknowledge user
- frappe.msgprint(_("Item valuation updated"))
-
- # Update Stock UOM
- def update_stock_uom(self):
- self.validate_mandatory()
- self.validate_uom_integer_type()
-
- self.update_stock_ledger_entry()
-
- self.update_bin()
-
- self.update_item_master()
-
-
+
def validate_uom_integer_type(self):
current_is_integer = frappe.db.get_value("UOM", self.current_stock_uom, "must_be_whole_number")
new_is_integer = frappe.db.get_value("UOM", self.new_stock_uom, "must_be_whole_number")
@@ -103,6 +60,53 @@
if current_is_integer and new_is_integer and cint(self.conversion_factor)!=self.conversion_factor:
frappe.throw(_("Conversion factor cannot be in fractions"))
+def update_item_master(item_code, new_stock_uom, conversion_factor):
+ frappe.db.set_value("Item", item_code, "stock_uom", new_stock_uom)
+ frappe.msgprint(_("Stock UOM updated for Item {0}").format(item_code))
+
+def update_bin(item_code, new_stock_uom, conversion_factor):
+ # update bin
+ if flt(conversion_factor) != flt(1):
+ frappe.db.sql("""update `tabBin`
+ set stock_uom = %s,
+ indented_qty = ifnull(indented_qty,0) * %s,
+ ordered_qty = ifnull(ordered_qty,0) * %s,
+ reserved_qty = ifnull(reserved_qty,0) * %s,
+ planned_qty = ifnull(planned_qty,0) * %s,
+ projected_qty = actual_qty + ordered_qty + indented_qty +
+ planned_qty - reserved_qty
+ where item_code = %s""", (new_stock_uom, conversion_factor,
+ conversion_factor, conversion_factor,
+ conversion_factor, item_code))
+ else:
+ frappe.db.sql("update `tabBin` set stock_uom = %s where item_code = %s",
+ (new_stock_uom, item_code) )
+
+def update_stock_ledger_entry(item_code, new_stock_uom, conversion_factor):
+ # update stock ledger entry
+ from erpnext.stock.stock_ledger import update_entries_after
+
+ if flt(conversion_factor) != flt(1):
+ frappe.db.sql("""update `tabStock Ledger Entry`
+ set stock_uom = %s, actual_qty = ifnull(actual_qty,0) * %s
+ where item_code = %s""",
+ (new_stock_uom, conversion_factor, item_code))
+ else:
+ frappe.db.sql("""update `tabStock Ledger Entry` set stock_uom=%s
+ where item_code=%s""", (new_stock_uom, item_code))
+
+ # acknowledge user
+ frappe.msgprint(_("Stock Ledger entries balances updated"))
+
+ # update item valuation
+ if flt(conversion_factor) != flt(1):
+ wh = frappe.db.sql("select name from `tabWarehouse`")
+ for w in wh:
+ update_entries_after({"item_code": item_code, "warehouse": w[0]})
+
+ # acknowledge user
+ frappe.msgprint(_("Item valuation updated"))
+
@frappe.whitelist()
def get_stock_uom(item_code):
return { 'current_stock_uom': cstr(frappe.db.get_value('Item', item_code, 'stock_uom')) }