mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-05 05:09:11 +00:00
- added barcode validation against Item Barcode
This commit is contained in:
@@ -2,30 +2,43 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
|
||||||
import erpnext
|
|
||||||
import json
|
|
||||||
import itertools
|
|
||||||
from frappe import msgprint, _
|
|
||||||
from frappe.utils import (cstr, flt, cint, getdate, now_datetime, formatdate,
|
|
||||||
strip, get_timestamp, random_string)
|
|
||||||
from frappe.utils.html_utils import clean_html
|
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
|
||||||
from erpnext.setup.doctype.item_group.item_group import invalidate_cache_for, get_parent_item_groups
|
|
||||||
from frappe.website.render import clear_cache
|
|
||||||
from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
|
|
||||||
from erpnext.controllers.item_variant import (get_variant, copy_attributes_to_variant,
|
|
||||||
make_variant_item_code, validate_item_variant_attributes, ItemVariantExistsError)
|
|
||||||
|
|
||||||
class DuplicateReorderRows(frappe.ValidationError): pass
|
import itertools
|
||||||
class StockExistsForTemplate(frappe.ValidationError): pass
|
import json
|
||||||
|
|
||||||
|
import erpnext
|
||||||
|
import frappe
|
||||||
|
from erpnext.controllers.item_variant import (ItemVariantExistsError,
|
||||||
|
copy_attributes_to_variant,
|
||||||
|
get_variant,
|
||||||
|
make_variant_item_code,
|
||||||
|
validate_item_variant_attributes)
|
||||||
|
from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups,
|
||||||
|
invalidate_cache_for)
|
||||||
|
from frappe import _, msgprint
|
||||||
|
from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate,
|
||||||
|
now_datetime, random_string, strip)
|
||||||
|
from frappe.utils.html_utils import clean_html
|
||||||
|
from frappe.website.doctype.website_slideshow.website_slideshow import \
|
||||||
|
get_slideshow
|
||||||
|
from frappe.website.render import clear_cache
|
||||||
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateReorderRows(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StockExistsForTemplate(frappe.ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Item(WebsiteGenerator):
|
class Item(WebsiteGenerator):
|
||||||
website = frappe._dict(
|
website = frappe._dict(
|
||||||
page_title_field = "item_name",
|
page_title_field="item_name",
|
||||||
condition_field = "show_in_website",
|
condition_field="show_in_website",
|
||||||
template = "templates/generators/item.html",
|
template="templates/generators/item.html",
|
||||||
no_cache = 1
|
no_cache=1
|
||||||
)
|
)
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
@@ -37,14 +50,14 @@ class Item(WebsiteGenerator):
|
|||||||
self.set_onload("asset_exists", True if asset else False)
|
self.set_onload("asset_exists", True if asset else False)
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
if frappe.db.get_default("item_naming_by")=="Naming Series":
|
if frappe.db.get_default("item_naming_by") == "Naming Series":
|
||||||
if self.variant_of:
|
if self.variant_of:
|
||||||
if not self.item_code:
|
if not self.item_code:
|
||||||
template_item_name = frappe.db.get_value("Item", self.variant_of, "item_name")
|
template_item_name = frappe.db.get_value("Item", self.variant_of, "item_name")
|
||||||
self.item_code = make_variant_item_code(self.variant_of, template_item_name, self)
|
self.item_code = make_variant_item_code(self.variant_of, template_item_name, self)
|
||||||
else:
|
else:
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
self.item_code = make_autoname(self.naming_series+'.#####')
|
self.item_code = make_autoname(self.naming_series + '.#####')
|
||||||
elif not self.item_code:
|
elif not self.item_code:
|
||||||
msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
|
msgprint(_("Item Code is mandatory because Item is not automatically numbered"), raise_exception=1)
|
||||||
|
|
||||||
@@ -85,7 +98,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.check_for_active_boms()
|
self.check_for_active_boms()
|
||||||
self.fill_customer_code()
|
self.fill_customer_code()
|
||||||
self.check_item_tax()
|
self.check_item_tax()
|
||||||
# self.validate_barcode()
|
self.validate_barcode()
|
||||||
self.validate_warehouse_for_reorder()
|
self.validate_warehouse_for_reorder()
|
||||||
self.update_bom_item_desc()
|
self.update_bom_item_desc()
|
||||||
self.synced_with_hub = 0
|
self.synced_with_hub = 0
|
||||||
@@ -176,7 +189,6 @@ class Item(WebsiteGenerator):
|
|||||||
"file_url": self.website_image
|
"file_url": self.website_image
|
||||||
}, fields=["name", "is_private"], order_by="is_private asc", limit_page_length=1)
|
}, fields=["name", "is_private"], order_by="is_private asc", limit_page_length=1)
|
||||||
|
|
||||||
|
|
||||||
if file_doc:
|
if file_doc:
|
||||||
file_doc = file_doc[0]
|
file_doc = file_doc[0]
|
||||||
|
|
||||||
@@ -219,7 +231,8 @@ class Item(WebsiteGenerator):
|
|||||||
self.website_image = None
|
self.website_image = None
|
||||||
|
|
||||||
except requests.exceptions.SSLError:
|
except requests.exceptions.SSLError:
|
||||||
frappe.msgprint(_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
|
frappe.msgprint(
|
||||||
|
_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
|
||||||
self.website_image = None
|
self.website_image = None
|
||||||
|
|
||||||
# for CSV import
|
# for CSV import
|
||||||
@@ -259,12 +272,13 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
def validate_retain_sample(self):
|
def validate_retain_sample(self):
|
||||||
if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
|
if self.retain_sample and not frappe.db.get_single_value('Stock Settings', 'sample_retention_warehouse'):
|
||||||
frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"));
|
frappe.throw(_("Please select Sample Retention Warehouse in Stock Settings first"))
|
||||||
if self.retain_sample and not self.has_batch_no:
|
if self.retain_sample and not self.has_batch_no:
|
||||||
frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(self.item_code))
|
frappe.throw(_(" {0} Retain Sample is based on batch, please check Has Batch No to retain sample of item").format(
|
||||||
|
self.item_code))
|
||||||
|
|
||||||
def get_context(self, context):
|
def get_context(self, context):
|
||||||
context.show_search=True
|
context.show_search = True
|
||||||
context.search_link = '/product_search'
|
context.search_link = '/product_search'
|
||||||
|
|
||||||
context.parents = get_parent_item_groups(self.item_group)
|
context.parents = get_parent_item_groups(self.item_group)
|
||||||
@@ -324,7 +338,7 @@ class Item(WebsiteGenerator):
|
|||||||
if attr.attribute_value not in values:
|
if attr.attribute_value not in values:
|
||||||
values.append(attr.attribute_value)
|
values.append(attr.attribute_value)
|
||||||
|
|
||||||
if v.name==context.variant.name:
|
if v.name == context.variant.name:
|
||||||
context.selected_attributes[attr.attribute] = attr.attribute_value
|
context.selected_attributes[attr.attribute] = attr.attribute_value
|
||||||
|
|
||||||
# filter attributes, order based on attribute table
|
# filter attributes, order based on attribute table
|
||||||
@@ -373,7 +387,7 @@ class Item(WebsiteGenerator):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
for i, attr in enumerate(self.attributes):
|
for i, attr in enumerate(self.attributes):
|
||||||
if i==0:
|
if i == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
combination_source = []
|
combination_source = []
|
||||||
@@ -422,12 +436,14 @@ class Item(WebsiteGenerator):
|
|||||||
check_list = []
|
check_list = []
|
||||||
for d in self.get('uoms'):
|
for d in self.get('uoms'):
|
||||||
if cstr(d.uom) in check_list:
|
if cstr(d.uom) in check_list:
|
||||||
frappe.throw(_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
|
frappe.throw(
|
||||||
|
_("Unit of Measure {0} has been entered more than once in Conversion Factor Table").format(d.uom))
|
||||||
else:
|
else:
|
||||||
check_list.append(cstr(d.uom))
|
check_list.append(cstr(d.uom))
|
||||||
|
|
||||||
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
|
if d.uom and cstr(d.uom) == cstr(self.stock_uom) and flt(d.conversion_factor) != 1:
|
||||||
frappe.throw(_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
|
frappe.throw(
|
||||||
|
_("Conversion factor for default Unit of Measure must be 1 in row {0}").format(d.idx))
|
||||||
|
|
||||||
def validate_item_type(self):
|
def validate_item_type(self):
|
||||||
if self.has_serial_no == 1 and self.is_stock_item == 0:
|
if self.has_serial_no == 1 and self.is_stock_item == 0:
|
||||||
@@ -436,35 +452,44 @@ class Item(WebsiteGenerator):
|
|||||||
if self.has_serial_no == 0 and self.serial_no_series:
|
if self.has_serial_no == 0 and self.serial_no_series:
|
||||||
self.serial_no_series = None
|
self.serial_no_series = None
|
||||||
|
|
||||||
|
|
||||||
def check_for_active_boms(self):
|
def check_for_active_boms(self):
|
||||||
if self.default_bom:
|
if self.default_bom:
|
||||||
bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
|
bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
|
||||||
if bom_item not in (self.name, self.variant_of):
|
if bom_item not in (self.name, self.variant_of):
|
||||||
frappe.throw(_("Default BOM ({0}) must be active for this item or its template").format(bom_item))
|
frappe.throw(
|
||||||
|
_("Default BOM ({0}) must be active for this item or its template").format(bom_item))
|
||||||
|
|
||||||
def fill_customer_code(self):
|
def fill_customer_code(self):
|
||||||
""" Append all the customer codes and insert into "customer_code" field of item table """
|
""" Append all the customer codes and insert into "customer_code" field of item table """
|
||||||
cust_code=[]
|
cust_code = []
|
||||||
for d in self.get('customer_items'):
|
for d in self.get('customer_items'):
|
||||||
cust_code.append(d.ref_code)
|
cust_code.append(d.ref_code)
|
||||||
self.customer_code=','.join(cust_code)
|
self.customer_code = ','.join(cust_code)
|
||||||
|
|
||||||
def check_item_tax(self):
|
def check_item_tax(self):
|
||||||
"""Check whether Tax Rate is not entered twice for same Tax Type"""
|
"""Check whether Tax Rate is not entered twice for same Tax Type"""
|
||||||
check_list=[]
|
check_list = []
|
||||||
for d in self.get('taxes'):
|
for d in self.get('taxes'):
|
||||||
if d.tax_type:
|
if d.tax_type:
|
||||||
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
|
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
|
||||||
|
|
||||||
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
|
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account']:
|
||||||
frappe.throw(_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
|
frappe.throw(
|
||||||
|
_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(d.idx))
|
||||||
else:
|
else:
|
||||||
if d.tax_type in check_list:
|
if d.tax_type in check_list:
|
||||||
frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
|
frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))
|
||||||
else:
|
else:
|
||||||
check_list.append(d.tax_type)
|
check_list.append(d.tax_type)
|
||||||
|
|
||||||
|
def validate_barcode(self):
|
||||||
|
if len(self.barcodes) > 0:
|
||||||
|
for barcode in self.barcodes:
|
||||||
|
duplicate = frappe.db.sql("""select name from `tabItem Barcode` where barcode = %s and parent != %s""", (barcode, self.name))
|
||||||
|
if duplicate:
|
||||||
|
frappe.throw(_("Barcode {0} already used in Item {1}").format(
|
||||||
|
self.barcode, duplicate[0][0]))
|
||||||
|
|
||||||
def validate_warehouse_for_reorder(self):
|
def validate_warehouse_for_reorder(self):
|
||||||
'''Validate Reorder level table for duplicate and conditional mandatory'''
|
'''Validate Reorder level table for duplicate and conditional mandatory'''
|
||||||
warehouse = []
|
warehouse = []
|
||||||
@@ -489,7 +514,8 @@ class Item(WebsiteGenerator):
|
|||||||
def validate_name_with_item_group(self):
|
def validate_name_with_item_group(self):
|
||||||
# causes problem with tree build
|
# causes problem with tree build
|
||||||
if frappe.db.exists("Item Group", self.name):
|
if frappe.db.exists("Item Group", self.name):
|
||||||
frappe.throw(_("An Item Group exists with same name, please change the item name or rename the item group"))
|
frappe.throw(
|
||||||
|
_("An Item Group exists with same name, please change the item name or rename the item group"))
|
||||||
|
|
||||||
def update_item_price(self):
|
def update_item_price(self):
|
||||||
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
||||||
@@ -504,7 +530,7 @@ class Item(WebsiteGenerator):
|
|||||||
frappe.delete_doc("Item", variant_of.name)
|
frappe.delete_doc("Item", variant_of.name)
|
||||||
|
|
||||||
def before_rename(self, old_name, new_name, merge=False):
|
def before_rename(self, old_name, new_name, merge=False):
|
||||||
if self.item_name==old_name:
|
if self.item_name == old_name:
|
||||||
frappe.db.set_value("Item", old_name, "item_name", new_name)
|
frappe.db.set_value("Item", old_name, "item_name", new_name)
|
||||||
|
|
||||||
if merge:
|
if merge:
|
||||||
@@ -573,7 +599,8 @@ class Item(WebsiteGenerator):
|
|||||||
row.description = desc
|
row.description = desc
|
||||||
|
|
||||||
def update_bom_item_desc(self):
|
def update_bom_item_desc(self):
|
||||||
if self.is_new(): return
|
if self.is_new():
|
||||||
|
return
|
||||||
|
|
||||||
if self.db_get('description') != self.description:
|
if self.db_get('description') != self.description:
|
||||||
frappe.db.sql("""
|
frappe.db.sql("""
|
||||||
@@ -616,7 +643,7 @@ class Item(WebsiteGenerator):
|
|||||||
return
|
return
|
||||||
if self.has_variants:
|
if self.has_variants:
|
||||||
updated = []
|
updated = []
|
||||||
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name })
|
variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name})
|
||||||
for d in variants:
|
for d in variants:
|
||||||
variant = frappe.get_doc("Item", d)
|
variant = frappe.get_doc("Item", d)
|
||||||
copy_attributes_to_variant(self, variant)
|
copy_attributes_to_variant(self, variant)
|
||||||
@@ -639,13 +666,14 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
if self.has_variants or self.variant_of:
|
if self.has_variants or self.variant_of:
|
||||||
if not self.is_child_table_same('attributes'):
|
if not self.is_child_table_same('attributes'):
|
||||||
frappe.throw(_('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
|
frappe.throw(
|
||||||
|
_('Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item'))
|
||||||
|
|
||||||
def validate_uom(self):
|
def validate_uom(self):
|
||||||
if not self.get("__islocal"):
|
if not self.get("__islocal"):
|
||||||
check_stock_uom_with_bin(self.name, self.stock_uom)
|
check_stock_uom_with_bin(self.name, self.stock_uom)
|
||||||
if self.has_variants:
|
if self.has_variants:
|
||||||
for d in frappe.db.get_all("Item", filters= {"variant_of": self.name}):
|
for d in frappe.db.get_all("Item", filters={"variant_of": self.name}):
|
||||||
check_stock_uom_with_bin(d.name, self.stock_uom)
|
check_stock_uom_with_bin(d.name, self.stock_uom)
|
||||||
if self.variant_of:
|
if self.variant_of:
|
||||||
template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
|
template_uom = frappe.db.get_value("Item", self.variant_of, "stock_uom")
|
||||||
@@ -654,18 +682,19 @@ class Item(WebsiteGenerator):
|
|||||||
.format(self.stock_uom, template_uom))
|
.format(self.stock_uom, template_uom))
|
||||||
|
|
||||||
def validate_attributes(self):
|
def validate_attributes(self):
|
||||||
if (self.has_variants or self.variant_of) and self.variant_based_on=='Item Attribute':
|
if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
|
||||||
attributes = []
|
attributes = []
|
||||||
if not self.attributes:
|
if not self.attributes:
|
||||||
frappe.throw(_("Attribute table is mandatory"))
|
frappe.throw(_("Attribute table is mandatory"))
|
||||||
for d in self.attributes:
|
for d in self.attributes:
|
||||||
if d.attribute in attributes:
|
if d.attribute in attributes:
|
||||||
frappe.throw(_("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
|
frappe.throw(
|
||||||
|
_("Attribute {0} selected multiple times in Attributes Table".format(d.attribute)))
|
||||||
else:
|
else:
|
||||||
attributes.append(d.attribute)
|
attributes.append(d.attribute)
|
||||||
|
|
||||||
def validate_variant_attributes(self):
|
def validate_variant_attributes(self):
|
||||||
if self.variant_of and self.variant_based_on=='Item Attribute':
|
if self.variant_of and self.variant_based_on == 'Item Attribute':
|
||||||
args = {}
|
args = {}
|
||||||
for d in self.attributes:
|
for d in self.attributes:
|
||||||
if not d.attribute_value:
|
if not d.attribute_value:
|
||||||
@@ -679,6 +708,7 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
validate_item_variant_attributes(self, args)
|
validate_item_variant_attributes(self, args)
|
||||||
|
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
'''returns timeline data based on stock ledger entry'''
|
'''returns timeline data based on stock ledger entry'''
|
||||||
out = {}
|
out = {}
|
||||||
@@ -689,21 +719,23 @@ def get_timeline_data(doctype, name):
|
|||||||
|
|
||||||
for date, count in items.iteritems():
|
for date, count in items.iteritems():
|
||||||
timestamp = get_timestamp(date)
|
timestamp = get_timestamp(date)
|
||||||
out.update({ timestamp: count })
|
out.update({timestamp: count})
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
|
def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
|
||||||
if (not end_of_life) or (disabled is None):
|
if (not end_of_life) or (disabled is None):
|
||||||
end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
|
end_of_life, disabled = frappe.db.get_value("Item", item_code, ["end_of_life", "disabled"])
|
||||||
|
|
||||||
if end_of_life and end_of_life!="0000-00-00" and getdate(end_of_life) <= now_datetime().date():
|
if end_of_life and end_of_life != "0000-00-00" and getdate(end_of_life) <= now_datetime().date():
|
||||||
msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
|
msg = _("Item {0} has reached its end of life on {1}").format(item_code, formatdate(end_of_life))
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
if disabled:
|
if disabled:
|
||||||
_msgprint(_("Item {0} is disabled").format(item_code), verbose)
|
_msgprint(_("Item {0} is disabled").format(item_code), verbose)
|
||||||
|
|
||||||
|
|
||||||
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
|
def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
|
||||||
if not is_stock_item:
|
if not is_stock_item:
|
||||||
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
|
is_stock_item = frappe.db.get_value("Item", item_code, "is_stock_item")
|
||||||
@@ -713,6 +745,7 @@ def validate_is_stock_item(item_code, is_stock_item=None, verbose=1):
|
|||||||
|
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
|
|
||||||
def validate_cancelled_item(item_code, docstatus=None, verbose=1):
|
def validate_cancelled_item(item_code, docstatus=None, verbose=1):
|
||||||
if docstatus is None:
|
if docstatus is None:
|
||||||
docstatus = frappe.db.get_value("Item", item_code, "docstatus")
|
docstatus = frappe.db.get_value("Item", item_code, "docstatus")
|
||||||
@@ -721,6 +754,7 @@ def validate_cancelled_item(item_code, docstatus=None, verbose=1):
|
|||||||
msg = _("Item {0} is cancelled").format(item_code)
|
msg = _("Item {0} is cancelled").format(item_code)
|
||||||
_msgprint(msg, verbose)
|
_msgprint(msg, verbose)
|
||||||
|
|
||||||
|
|
||||||
def _msgprint(msg, verbose):
|
def _msgprint(msg, verbose):
|
||||||
if verbose:
|
if verbose:
|
||||||
msgprint(msg, raise_exception=True)
|
msgprint(msg, raise_exception=True)
|
||||||
@@ -752,9 +786,9 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
|||||||
order by pr.posting_date desc, pr.posting_time desc, pr.name desc
|
order by pr.posting_date desc, pr.posting_time desc, pr.name desc
|
||||||
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
|
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
|
||||||
|
|
||||||
purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date \
|
purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
|
||||||
or "1900-01-01")
|
or "1900-01-01")
|
||||||
purchase_receipt_date = getdate(last_purchase_receipt and \
|
purchase_receipt_date = getdate(last_purchase_receipt and
|
||||||
last_purchase_receipt[0].posting_date or "1900-01-01")
|
last_purchase_receipt[0].posting_date or "1900-01-01")
|
||||||
|
|
||||||
if (purchase_order_date > purchase_receipt_date) or \
|
if (purchase_order_date > purchase_receipt_date) or \
|
||||||
@@ -789,11 +823,12 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def invalidate_cache_for_item(doc):
|
def invalidate_cache_for_item(doc):
|
||||||
invalidate_cache_for(doc, doc.item_group)
|
invalidate_cache_for(doc, doc.item_group)
|
||||||
|
|
||||||
website_item_groups = list(set((doc.get("old_website_item_groups") or [])
|
website_item_groups = list(set((doc.get("old_website_item_groups") or [])
|
||||||
+ [d.item_group for d in doc.get({"doctype":"Website Item Group"}) if d.item_group]))
|
+ [d.item_group for d in doc.get({"doctype": "Website Item Group"}) if d.item_group]))
|
||||||
|
|
||||||
for item_group in website_item_groups:
|
for item_group in website_item_groups:
|
||||||
invalidate_cache_for(doc, item_group)
|
invalidate_cache_for(doc, item_group)
|
||||||
@@ -801,11 +836,12 @@ def invalidate_cache_for_item(doc):
|
|||||||
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
|
if doc.get("old_item_group") and doc.get("old_item_group") != doc.item_group:
|
||||||
invalidate_cache_for(doc, doc.old_item_group)
|
invalidate_cache_for(doc, doc.old_item_group)
|
||||||
|
|
||||||
|
|
||||||
def check_stock_uom_with_bin(item, stock_uom):
|
def check_stock_uom_with_bin(item, stock_uom):
|
||||||
if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
|
if stock_uom == frappe.db.get_value("Item", item, "stock_uom"):
|
||||||
return
|
return
|
||||||
|
|
||||||
matched=True
|
matched = True
|
||||||
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
ref_uom = frappe.db.get_value("Stock Ledger Entry",
|
||||||
{"item_code": item}, "stock_uom")
|
{"item_code": item}, "stock_uom")
|
||||||
|
|
||||||
@@ -815,7 +851,7 @@ def check_stock_uom_with_bin(item, stock_uom):
|
|||||||
else:
|
else:
|
||||||
bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
|
bin_list = frappe.db.sql("select * from tabBin where item_code=%s", item, as_dict=1)
|
||||||
for bin in bin_list:
|
for bin in bin_list:
|
||||||
if (bin.reserved_qty > 0 or bin.ordered_qty > 0 or bin.indented_qty > 0 \
|
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):
|
or bin.planned_qty > 0) and cstr(bin.stock_uom) != cstr(stock_uom):
|
||||||
matched = False
|
matched = False
|
||||||
break
|
break
|
||||||
@@ -824,4 +860,5 @@ def check_stock_uom_with_bin(item, stock_uom):
|
|||||||
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
|
frappe.db.sql("""update tabBin set stock_uom=%s where item_code=%s""", (stock_uom, item))
|
||||||
|
|
||||||
if not matched:
|
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. You will need to create a new Item to use a different Default UOM.").format(item))
|
frappe.throw(
|
||||||
|
_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.").format(item))
|
||||||
|
|||||||
Reference in New Issue
Block a user