Merge branch 'develop'
This commit is contained in:
commit
c85a37fe00
@ -1,2 +1,2 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
__version__ = '6.5.1'
|
__version__ = '6.5.2'
|
||||||
|
|||||||
@ -162,6 +162,7 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -186,6 +187,7 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -511,7 +513,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"modified": "2015-10-02 07:38:51.117114",
|
"modified": "2015-10-20 12:10:59.771050",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GL Entry",
|
"name": "GL Entry",
|
||||||
|
|||||||
@ -276,7 +276,7 @@
|
|||||||
"label": "Exchange Rate",
|
"label": "Exchange Rate",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "6",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -345,6 +345,7 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -412,6 +413,7 @@
|
|||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
@ -563,7 +565,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-02 07:38:54.406370",
|
"modified": "2015-10-20 12:10:22.203395",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry Account",
|
"name": "Journal Entry Account",
|
||||||
|
|||||||
@ -51,7 +51,7 @@ def validate_item_variant_attributes(item, args):
|
|||||||
frappe.throw(_("Increment for Attribute {0} cannot be 0").format(attribute))
|
frappe.throw(_("Increment for Attribute {0} cannot be 0").format(attribute))
|
||||||
|
|
||||||
is_in_range = from_range <= flt(value) <= to_range
|
is_in_range = from_range <= flt(value) <= to_range
|
||||||
precision = len(cstr(increment).split(".")[-1].rstrip("0"))
|
precision = max(len(cstr(v).split(".")[-1].rstrip("0")) for v in (value, increment))
|
||||||
#avoid precision error by rounding the remainder
|
#avoid precision error by rounding the remainder
|
||||||
remainder = flt((flt(value) - from_range) % increment, precision)
|
remainder = flt((flt(value) - from_range) % increment, precision)
|
||||||
|
|
||||||
@ -125,12 +125,11 @@ def copy_attributes_to_variant(item, variant):
|
|||||||
from frappe.model import no_value_fields
|
from frappe.model import no_value_fields
|
||||||
for field in item.meta.fields:
|
for field in item.meta.fields:
|
||||||
if field.fieldtype not in no_value_fields and (not field.no_copy)\
|
if field.fieldtype not in no_value_fields and (not field.no_copy)\
|
||||||
and field.fieldname not in ("item_code", "item_name"):
|
and field.fieldname not in ("item_code", "item_name", "show_in_website"):
|
||||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||||
variant.set(field.fieldname, item.get(field.fieldname))
|
variant.set(field.fieldname, item.get(field.fieldname))
|
||||||
variant.variant_of = item.name
|
variant.variant_of = item.name
|
||||||
variant.has_variants = 0
|
variant.has_variants = 0
|
||||||
variant.show_in_website = 0
|
|
||||||
if variant.attributes:
|
if variant.attributes:
|
||||||
variant.description += "\n"
|
variant.description += "\n"
|
||||||
for d in variant.attributes:
|
for d in variant.attributes:
|
||||||
|
|||||||
@ -29,7 +29,7 @@ blogs.
|
|||||||
"""
|
"""
|
||||||
app_icon = "icon-th"
|
app_icon = "icon-th"
|
||||||
app_color = "#e74c3c"
|
app_color = "#e74c3c"
|
||||||
app_version = "6.5.1"
|
app_version = "6.5.2"
|
||||||
github_link = "https://github.com/frappe/erpnext"
|
github_link = "https://github.com/frappe/erpnext"
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
|||||||
@ -14,7 +14,8 @@ frappe.views.calendar["Holiday List"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "fiscal_year",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"label": __("Fiscal Year")
|
"label": __("Fiscal Year"),
|
||||||
|
"default": frappe.defaults.get_user_default("fiscal_year")
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
get_events_method: "erpnext.hr.doctype.holiday_list.holiday_list.get_events"
|
get_events_method: "erpnext.hr.doctype.holiday_list.holiday_list.get_events"
|
||||||
|
|||||||
@ -224,5 +224,8 @@ erpnext.patches.v6_4.email_digest_update
|
|||||||
execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory")
|
execute:frappe.delete_doc_if_exists("DocType", "Applicable Territory")
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
|
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Price List")
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
|
execute:frappe.delete_doc_if_exists("DocType", "Shopping Cart Taxes and Charges Master")
|
||||||
|
|
||||||
erpnext.patches.v6_4.set_user_in_contact
|
erpnext.patches.v6_4.set_user_in_contact
|
||||||
erpnext.patches.v6_4.make_image_thumbnail
|
erpnext.patches.v6_4.make_image_thumbnail
|
||||||
|
|
||||||
|
erpnext.patches.v6_5.show_in_website_for_template_item
|
||||||
|
|||||||
1
erpnext/patches/v6_5/__init__.py
Normal file
1
erpnext/patches/v6_5/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
15
erpnext/patches/v6_5/show_in_website_for_template_item.py
Normal file
15
erpnext/patches/v6_5/show_in_website_for_template_item.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
import frappe.website.render
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
for item_code in frappe.db.sql_list("""select distinct variant_of from `tabItem`
|
||||||
|
where variant_of is not null and variant_of !='' and show_in_website=1"""):
|
||||||
|
|
||||||
|
item = frappe.get_doc("Item", item_code)
|
||||||
|
item.db_set("show_in_website", 1, update_modified=False)
|
||||||
|
|
||||||
|
item.get_route()
|
||||||
|
item.db_set("page_name", item.page_name, update_modified=False)
|
||||||
|
|
||||||
|
frappe.website.render.clear_cache()
|
||||||
@ -240,9 +240,9 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
|
|
||||||
make_item("_Test Service Product Bundle", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Service Product Bundle", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
|
|
||||||
make_product_bundle("_Test Service Product Bundle",
|
make_product_bundle("_Test Service Product Bundle",
|
||||||
["_Test Service Product Bundle Item 1", "_Test Service Product Bundle Item 2"])
|
["_Test Service Product Bundle Item 1", "_Test Service Product Bundle Item 2"])
|
||||||
@ -256,9 +256,9 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||||
|
|
||||||
make_item("_Test Mix Product Bundle", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Mix Product Bundle", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1, "is_sales_item": 1})
|
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1, "is_sales_item": 1})
|
||||||
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
|
|
||||||
make_product_bundle("_Test Mix Product Bundle",
|
make_product_bundle("_Test Mix Product Bundle",
|
||||||
["_Test Mix Product Bundle Item 1", "_Test Mix Product Bundle Item 2"])
|
["_Test Mix Product Bundle Item 1", "_Test Mix Product Bundle Item 2"])
|
||||||
@ -267,7 +267,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
def test_auto_insert_price(self):
|
def test_auto_insert_price(self):
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
make_item("_Test Item for Auto Price List", {"is_stock_item": 0, "is_sales_item": 1})
|
make_item("_Test Item for Auto Price List", {"is_stock_item": 0, "is_pro_applicable": 0, "is_sales_item": 1})
|
||||||
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
||||||
|
|
||||||
item_price = frappe.db.get_value("Item Price", {"price_list": "_Test Price List",
|
item_price = frappe.db.get_value("Item Price", {"price_list": "_Test Price List",
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<h1 style="{{ h1 }}">{{ title }}</h1>
|
<h1 style="{{ h1 }}">{{ title }}</h1>
|
||||||
<h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;">
|
<h4 style="font-weight: normal; color: {{ text_muted }}; margin-top: 7px; font-size: 16px; margin-top: 7px;">
|
||||||
<p>{% if frequency == "Daily "%}
|
<p>{% if frequency == "Daily" %}
|
||||||
{{ frappe.format_date(future_from_date) }}
|
{{ frappe.format_date(future_from_date) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ frappe.format_date(future_from_date) }} - {{ frappe.format_date(future_to_date) }}
|
{{ frappe.format_date(future_from_date) }} - {{ frappe.format_date(future_to_date) }}
|
||||||
|
|||||||
@ -292,6 +292,7 @@ def get_customer(user=None):
|
|||||||
"customer_group": get_shopping_cart_settings().default_customer_group,
|
"customer_group": get_shopping_cart_settings().default_customer_group,
|
||||||
"territory": get_root_of("Territory")
|
"territory": get_root_of("Territory")
|
||||||
})
|
})
|
||||||
|
customer.ignore_mandatory = True
|
||||||
customer.insert(ignore_permissions=True)
|
customer.insert(ignore_permissions=True)
|
||||||
|
|
||||||
contact = frappe.new_doc("Contact")
|
contact = frappe.new_doc("Contact")
|
||||||
@ -300,6 +301,7 @@ def get_customer(user=None):
|
|||||||
"first_name": fullname,
|
"first_name": fullname,
|
||||||
"email_id": user
|
"email_id": user
|
||||||
})
|
})
|
||||||
|
contact.ignore_mandatory = True
|
||||||
contact.insert(ignore_permissions=True)
|
contact.insert(ignore_permissions=True)
|
||||||
|
|
||||||
return customer
|
return customer
|
||||||
|
|||||||
@ -14,25 +14,11 @@ def get_product_info(item_code):
|
|||||||
if not is_cart_enabled():
|
if not is_cart_enabled():
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
cart_quotation = _get_cart_quotation()
|
|
||||||
|
|
||||||
price_list = cart_quotation.selling_price_list
|
|
||||||
|
|
||||||
warehouse = frappe.db.get_value("Item", item_code, "website_warehouse")
|
|
||||||
if warehouse:
|
|
||||||
in_stock = frappe.db.sql("""select actual_qty from tabBin where
|
|
||||||
item_code=%s and warehouse=%s""", (item_code, warehouse))
|
|
||||||
if in_stock:
|
|
||||||
in_stock = in_stock[0][0] > 0 and 1 or 0
|
|
||||||
else:
|
|
||||||
in_stock = -1
|
|
||||||
|
|
||||||
price = price_list and frappe.db.sql("""select price_list_rate, currency from
|
|
||||||
`tabItem Price` where item_code=%s and price_list=%s""",
|
|
||||||
(item_code, price_list), as_dict=1) or []
|
|
||||||
|
|
||||||
price = price and price[0] or None
|
|
||||||
qty = 0
|
qty = 0
|
||||||
|
cart_quotation = _get_cart_quotation()
|
||||||
|
template_item_code = frappe.db.get_value("Item", item_code, "variant_of")
|
||||||
|
in_stock = get_qty_in_stock(item_code, template_item_code)
|
||||||
|
price = get_price(item_code, template_item_code, cart_quotation.selling_price_list)
|
||||||
|
|
||||||
if price:
|
if price:
|
||||||
price["formatted_price"] = fmt_money(price["price_list_rate"], currency=price["currency"])
|
price["formatted_price"] = fmt_money(price["price_list_rate"], currency=price["currency"])
|
||||||
@ -52,3 +38,31 @@ def get_product_info(item_code):
|
|||||||
"uom": frappe.db.get_value("Item", item_code, "stock_uom"),
|
"uom": frappe.db.get_value("Item", item_code, "stock_uom"),
|
||||||
"qty": qty
|
"qty": qty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_qty_in_stock(item_code, template_item_code):
|
||||||
|
warehouse = frappe.db.get_value("Item", item_code, "website_warehouse")
|
||||||
|
if not warehouse and template_item_code and template_item_code != item_code:
|
||||||
|
warehouse = frappe.db.get_value("Item", template_item_code, "website_warehouse")
|
||||||
|
|
||||||
|
if warehouse:
|
||||||
|
in_stock = frappe.db.sql("""select actual_qty from tabBin where
|
||||||
|
item_code=%s and warehouse=%s""", (item_code, warehouse))
|
||||||
|
if in_stock:
|
||||||
|
in_stock = in_stock[0][0] > 0 and 1 or 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
in_stock = -1
|
||||||
|
|
||||||
|
return in_stock
|
||||||
|
|
||||||
|
def get_price(item_code, template_item_code, price_list):
|
||||||
|
if price_list:
|
||||||
|
price = frappe.get_all("Item Price", fields=["price_list_rate", "currency"],
|
||||||
|
filters={"price_list": price_list, "item_code": item_code})
|
||||||
|
|
||||||
|
if not price:
|
||||||
|
price = frappe.get_all("Item Price", fields=["price_list_rate", "currency"],
|
||||||
|
filters={"price_list": price_list, "item_code": template_item_code})
|
||||||
|
|
||||||
|
if price:
|
||||||
|
return price[0]
|
||||||
|
|||||||
@ -1647,7 +1647,7 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"default": "",
|
"default": "1",
|
||||||
"depends_on": "",
|
"depends_on": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"fieldname": "is_pro_applicable",
|
"fieldname": "is_pro_applicable",
|
||||||
@ -2113,7 +2113,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2015-10-19 03:04:49.445770",
|
"modified": "2015-10-20 12:14:43.315827",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
import json
|
import json
|
||||||
|
import urllib
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate
|
from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
@ -79,6 +80,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.validate_name_with_item_group()
|
self.validate_name_with_item_group()
|
||||||
self.update_item_price()
|
self.update_item_price()
|
||||||
self.update_variants()
|
self.update_variants()
|
||||||
|
self.update_template_item()
|
||||||
|
|
||||||
def make_thumbnail(self):
|
def make_thumbnail(self):
|
||||||
"""Make a thumbnail of `website_image`"""
|
"""Make a thumbnail of `website_image`"""
|
||||||
@ -86,11 +88,16 @@ class Item(WebsiteGenerator):
|
|||||||
self.thumbnail = None
|
self.thumbnail = None
|
||||||
|
|
||||||
if self.website_image and not self.thumbnail:
|
if self.website_image and not self.thumbnail:
|
||||||
file_doc = frappe.get_doc("File", {
|
file_doc = None
|
||||||
"file_url": self.website_image,
|
|
||||||
"attached_to_doctype": "Item",
|
try:
|
||||||
"attached_to_name": self.name
|
file_doc = frappe.get_doc("File", {
|
||||||
})
|
"file_url": self.website_image,
|
||||||
|
"attached_to_doctype": "Item",
|
||||||
|
"attached_to_name": self.name
|
||||||
|
})
|
||||||
|
except frappe.DoesNotExistError:
|
||||||
|
pass
|
||||||
|
|
||||||
# for CSV import
|
# for CSV import
|
||||||
if not file_doc:
|
if not file_doc:
|
||||||
@ -108,18 +115,59 @@ class Item(WebsiteGenerator):
|
|||||||
self.thumbnail = file_doc.thumbnail_url
|
self.thumbnail = file_doc.thumbnail_url
|
||||||
|
|
||||||
def get_context(self, context):
|
def get_context(self, context):
|
||||||
|
if self.variant_of:
|
||||||
|
# redirect to template page!
|
||||||
|
template_item = frappe.get_doc("Item", self.variant_of)
|
||||||
|
frappe.flags.redirect_location = template_item.get_route() + "?variant=" + urllib.quote(self.name)
|
||||||
|
raise frappe.Redirect
|
||||||
|
|
||||||
context.parent_groups = get_parent_item_groups(self.item_group) + \
|
context.parent_groups = get_parent_item_groups(self.item_group) + \
|
||||||
[{"name": self.name}]
|
[{"name": self.name}]
|
||||||
if self.slideshow:
|
|
||||||
context.update(get_slideshow(self))
|
|
||||||
|
|
||||||
|
self.set_variant_context(context)
|
||||||
|
|
||||||
|
self.set_attribute_context(context)
|
||||||
|
|
||||||
|
context.parents = self.get_parents(context)
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def set_variant_context(self, context):
|
||||||
|
if self.has_variants:
|
||||||
|
context.no_cache = True
|
||||||
|
|
||||||
|
# load variants
|
||||||
|
# also used in set_attribute_context
|
||||||
|
context.variants = frappe.get_all("Item",
|
||||||
|
filters={"variant_of": self.name, "show_in_website": 1}, order_by="name asc")
|
||||||
|
|
||||||
|
variant = frappe.form_dict.variant
|
||||||
|
if not variant:
|
||||||
|
# the case when the item is opened for the first time from its list
|
||||||
|
variant = context.variants[0]
|
||||||
|
|
||||||
|
context.variant = frappe.get_doc("Item", variant)
|
||||||
|
|
||||||
|
for fieldname in ("website_image", "web_long_description", "description",
|
||||||
|
"website_specifications"):
|
||||||
|
if context.variant.get(fieldname):
|
||||||
|
value = context.variant.get(fieldname)
|
||||||
|
if isinstance(value, list):
|
||||||
|
value = [d.as_dict() for d in value]
|
||||||
|
|
||||||
|
context[fieldname] = value
|
||||||
|
|
||||||
|
if self.slideshow:
|
||||||
|
if context.variant and context.variant.slideshow:
|
||||||
|
context.update(get_slideshow(context.variant))
|
||||||
|
else:
|
||||||
|
context.update(get_slideshow(self))
|
||||||
|
|
||||||
|
def set_attribute_context(self, context):
|
||||||
if self.has_variants:
|
if self.has_variants:
|
||||||
attribute_values_available = {}
|
attribute_values_available = {}
|
||||||
context.attribute_values = {}
|
context.attribute_values = {}
|
||||||
|
context.selected_attributes = {}
|
||||||
# load variants
|
|
||||||
context.variants = frappe.get_all("Item",
|
|
||||||
filters={"variant_of": self.name, "show_in_website": 1})
|
|
||||||
|
|
||||||
# load attributes
|
# load attributes
|
||||||
for v in context.variants:
|
for v in context.variants:
|
||||||
@ -131,6 +179,9 @@ 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:
|
||||||
|
context.selected_attributes[attr.attribute] = attr.attribute_value
|
||||||
|
|
||||||
# filter attributes, order based on attribute table
|
# filter attributes, order based on attribute table
|
||||||
for attr in self.attributes:
|
for attr in self.attributes:
|
||||||
values = context.attribute_values.setdefault(attr.attribute, [])
|
values = context.attribute_values.setdefault(attr.attribute, [])
|
||||||
@ -144,10 +195,6 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
context.variant_info = json.dumps(context.variants)
|
context.variant_info = json.dumps(context.variants)
|
||||||
|
|
||||||
context.parents = self.get_parents(context)
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
def check_warehouse_is_set_for_stock_item(self):
|
def check_warehouse_is_set_for_stock_item(self):
|
||||||
if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
|
if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
|
||||||
frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
|
frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
|
||||||
@ -355,6 +402,16 @@ class Item(WebsiteGenerator):
|
|||||||
frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where
|
frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where
|
||||||
item_code = %s and docstatus < 2""",(self.description, self.name))
|
item_code = %s and docstatus < 2""",(self.description, self.name))
|
||||||
|
|
||||||
|
def update_template_item(self):
|
||||||
|
"""Set Show in Website for Template Item if True for its Variant"""
|
||||||
|
if self.variant_of and self.show_in_website:
|
||||||
|
template_item = frappe.get_doc("Item", self.variant_of)
|
||||||
|
|
||||||
|
if not template_item.show_in_website:
|
||||||
|
template_item.show_in_website = 1
|
||||||
|
template_item.flags.ignore_permissions = True
|
||||||
|
template_item.save()
|
||||||
|
|
||||||
def update_variants(self):
|
def update_variants(self):
|
||||||
if self.has_variants:
|
if self.has_variants:
|
||||||
updated = []
|
updated = []
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@ -216,7 +216,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-02 07:38:54.612684",
|
"modified": "2015-10-20 12:16:35.511560",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Item",
|
"name": "Landed Cost Item",
|
||||||
|
|||||||
@ -15,21 +15,37 @@ def reorder_item():
|
|||||||
|
|
||||||
def _reorder_item():
|
def _reorder_item():
|
||||||
material_requests = {"Purchase": {}, "Transfer": {}}
|
material_requests = {"Purchase": {}, "Transfer": {}}
|
||||||
|
warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`
|
||||||
item_warehouse_projected_qty = get_item_warehouse_projected_qty()
|
where disabled=0"""))
|
||||||
|
|
||||||
warehouse_company = frappe._dict(frappe.db.sql("""select name, company from `tabWarehouse`"""))
|
|
||||||
default_company = (frappe.defaults.get_defaults().get("company") or
|
default_company = (frappe.defaults.get_defaults().get("company") or
|
||||||
frappe.db.sql("""select name from tabCompany limit 1""")[0][0])
|
frappe.db.sql("""select name from tabCompany limit 1""")[0][0])
|
||||||
|
|
||||||
|
items_to_consider = frappe.db.sql_list("""select name from `tabItem` item
|
||||||
|
where is_stock_item=1 and has_variants=0
|
||||||
|
and (is_purchase_item=1 or is_sub_contracted_item=1)
|
||||||
|
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s)
|
||||||
|
and ((re_order_level is not null and re_order_level > 0)
|
||||||
|
or exists (select name from `tabItem Reorder` ir where ir.parent=item.name)
|
||||||
|
or (variant_of is not null and variant_of != ''
|
||||||
|
and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of))
|
||||||
|
)""",
|
||||||
|
{"today": nowdate()})
|
||||||
|
|
||||||
|
if not items_to_consider:
|
||||||
|
return
|
||||||
|
|
||||||
|
item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider)
|
||||||
|
|
||||||
def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type):
|
def add_to_material_request(item_code, warehouse, reorder_level, reorder_qty, material_request_type):
|
||||||
if warehouse not in item_warehouse_projected_qty[item_code]:
|
if warehouse not in warehouse_company:
|
||||||
# likely a disabled warehouse or a warehouse where BIN does not exist
|
# a disabled warehouse
|
||||||
return
|
return
|
||||||
|
|
||||||
reorder_level = flt(reorder_level)
|
reorder_level = flt(reorder_level)
|
||||||
reorder_qty = flt(reorder_qty)
|
reorder_qty = flt(reorder_qty)
|
||||||
projected_qty = item_warehouse_projected_qty[item_code][warehouse]
|
|
||||||
|
# projected_qty will be 0 if Bin does not exist
|
||||||
|
projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse))
|
||||||
|
|
||||||
if reorder_level and projected_qty < reorder_level:
|
if reorder_level and projected_qty < reorder_level:
|
||||||
deficiency = reorder_level - projected_qty
|
deficiency = reorder_level - projected_qty
|
||||||
@ -44,7 +60,7 @@ def _reorder_item():
|
|||||||
"reorder_qty": reorder_qty
|
"reorder_qty": reorder_qty
|
||||||
})
|
})
|
||||||
|
|
||||||
for item_code in item_warehouse_projected_qty:
|
for item_code in items_to_consider:
|
||||||
item = frappe.get_doc("Item", item_code)
|
item = frappe.get_doc("Item", item_code)
|
||||||
|
|
||||||
if item.variant_of and not item.get("reorder_levels"):
|
if item.variant_of and not item.get("reorder_levels"):
|
||||||
@ -62,18 +78,13 @@ def _reorder_item():
|
|||||||
if material_requests:
|
if material_requests:
|
||||||
return create_material_request(material_requests)
|
return create_material_request(material_requests)
|
||||||
|
|
||||||
def get_item_warehouse_projected_qty():
|
def get_item_warehouse_projected_qty(items_to_consider):
|
||||||
item_warehouse_projected_qty = {}
|
item_warehouse_projected_qty = {}
|
||||||
|
|
||||||
for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, warehouse, projected_qty
|
for item_code, warehouse, projected_qty in frappe.db.sql("""select item_code, warehouse, projected_qty
|
||||||
from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''
|
from tabBin where item_code in ({0})
|
||||||
and exists (select name from `tabItem`
|
and (warehouse != "" and warehouse is not null)"""\
|
||||||
where `tabItem`.name = `tabBin`.item_code and
|
.format(", ".join(["%s"] * len(items_to_consider))), items_to_consider):
|
||||||
is_stock_item=1 and (is_purchase_item=1 or is_sub_contracted_item=1) and
|
|
||||||
(ifnull(end_of_life, '0000-00-00')='0000-00-00' or end_of_life > %s))
|
|
||||||
and exists (select name from `tabWarehouse`
|
|
||||||
where `tabWarehouse`.name = `tabBin`.warehouse
|
|
||||||
and ifnull(disabled, 0)=0)""", nowdate()):
|
|
||||||
|
|
||||||
item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty)
|
item_warehouse_projected_qty.setdefault(item_code, {})[warehouse] = flt(projected_qty)
|
||||||
|
|
||||||
|
|||||||
@ -6,25 +6,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
columns = get_columns()
|
filters = frappe._dict(filters or {})
|
||||||
|
return get_columns(), get_data(filters)
|
||||||
data = frappe.db.sql("""select
|
|
||||||
item.name, item.item_name, description, item_group, brand, warehouse, item.stock_uom,
|
|
||||||
actual_qty, planned_qty, indented_qty, ordered_qty, reserved_qty,
|
|
||||||
projected_qty, item.re_order_level, item.re_order_qty,
|
|
||||||
(item.re_order_level - projected_qty) as shortage_qty
|
|
||||||
from `tabBin` bin,
|
|
||||||
(select name, company from tabWarehouse
|
|
||||||
{warehouse_conditions}) wh,
|
|
||||||
(select name, item_name, description, stock_uom, item_group,
|
|
||||||
brand, re_order_level, re_order_qty
|
|
||||||
from `tabItem` {item_conditions}) item
|
|
||||||
where item_code = item.name and warehouse = wh.name
|
|
||||||
order by item.name, wh.name"""\
|
|
||||||
.format(item_conditions=get_item_conditions(filters),
|
|
||||||
warehouse_conditions=get_warehouse_conditions(filters)), filters)
|
|
||||||
|
|
||||||
return columns, data
|
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
return [_("Item Code") + ":Link/Item:140", _("Item Name") + "::100", _("Description") + "::200",
|
return [_("Item Code") + ":Link/Item:140", _("Item Name") + "::100", _("Description") + "::200",
|
||||||
@ -34,20 +17,47 @@ def get_columns():
|
|||||||
_("Projected Qty") + ":Float:100", _("Reorder Level") + ":Float:100", _("Reorder Qty") + ":Float:100",
|
_("Projected Qty") + ":Float:100", _("Reorder Level") + ":Float:100", _("Reorder Qty") + ":Float:100",
|
||||||
_("Shortage Qty") + ":Float:100"]
|
_("Shortage Qty") + ":Float:100"]
|
||||||
|
|
||||||
def get_item_conditions(filters):
|
def get_data(filters):
|
||||||
conditions = []
|
item_map = {}
|
||||||
if filters.get("item_code"):
|
warehouse_company = {}
|
||||||
conditions.append("name=%(item_code)s")
|
data = []
|
||||||
if filters.get("brand"):
|
|
||||||
conditions.append("brand=%(brand)s")
|
|
||||||
|
|
||||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
for bin in get_bin_list(filters):
|
||||||
|
item = item_map.setdefault(bin.item_code, frappe.get_doc("Item", bin.item_code))
|
||||||
|
company = warehouse_company.setdefault(bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company"))
|
||||||
|
|
||||||
def get_warehouse_conditions(filters):
|
if filters.brand and filters.brand != item.brand:
|
||||||
conditions = []
|
continue
|
||||||
if filters.get("company"):
|
|
||||||
conditions.append("company=%(company)s")
|
|
||||||
if filters.get("warehouse"):
|
|
||||||
conditions.append("name=%(warehouse)s")
|
|
||||||
|
|
||||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
elif filters.company and filters.company != company:
|
||||||
|
continue
|
||||||
|
|
||||||
|
re_order_level = re_order_qty = 0
|
||||||
|
|
||||||
|
if bin.warehouse==item.default_warehouse:
|
||||||
|
re_order_level = item.re_order_level or 0
|
||||||
|
re_order_qty = item.re_order_qty or 0
|
||||||
|
|
||||||
|
for d in item.get("reorder_levels"):
|
||||||
|
if d.warehouse == bin.warehouse:
|
||||||
|
re_order_level = d.warehouse_reorder_level
|
||||||
|
re_order_qty = d.warehouse_reorder_qty
|
||||||
|
|
||||||
|
data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
|
||||||
|
item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty,
|
||||||
|
bin.projected_qty, re_order_level, re_order_qty, re_order_level - bin.projected_qty])
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_bin_list(filters):
|
||||||
|
bin_filters = frappe._dict()
|
||||||
|
if filters.item_code:
|
||||||
|
bin_filters.item_code = filters.item_code
|
||||||
|
if filters.warehouse:
|
||||||
|
bin_filters.warehouse = filters.warehouse
|
||||||
|
|
||||||
|
bin_list = frappe.get_all("Bin", fields=["item_code", "warehouse",
|
||||||
|
"actual_qty", "planned_qty", "indented_qty", "ordered_qty", "reserved_qty", "projected_qty"],
|
||||||
|
filters=bin_filters, order_by="item_code, warehouse")
|
||||||
|
|
||||||
|
return bin_list
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
frappe.ui.form.on("Issue", {
|
frappe.ui.form.on("Issue", {
|
||||||
|
"onload": function(frm) {
|
||||||
|
frm.email_field = "raised_by";
|
||||||
|
},
|
||||||
|
|
||||||
"refresh": function(frm) {
|
"refresh": function(frm) {
|
||||||
if(frm.doc.status==="Open") {
|
if(frm.doc.status==="Open") {
|
||||||
frm.add_custom_button("Close", function() {
|
frm.add_custom_button("Close", function() {
|
||||||
|
|||||||
@ -38,8 +38,11 @@
|
|||||||
<select class="form-control"
|
<select class="form-control"
|
||||||
style="max-width: 140px"
|
style="max-width: 140px"
|
||||||
data-attribute="{{ d.attribute }}">
|
data-attribute="{{ d.attribute }}">
|
||||||
{% for value in attribute_values[d.attribute] %}
|
{% for value in attribute_values[d.attribute] %}
|
||||||
<option value="{{ value }}">{{ _(value) }}</option>
|
<option value="{{ value }}"
|
||||||
|
{% if selected_attributes and selected_attributes[d.attribute]==value -%} selected {%- endif %}>
|
||||||
|
{{ _(value) }}
|
||||||
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -71,13 +74,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if doc.get({"doctype":"Item Website Specification"}) -%}
|
{% if website_specifications -%}
|
||||||
<div class="row" style="margin-top: 20px">
|
<div class="row item-website-specification" style="margin-top: 20px">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<h4>{{ _("Specifications") }}</h4>
|
<h4>{{ _("Specifications") }}</h4>
|
||||||
|
|
||||||
<table class="table table-bordered" style="width: 100%">
|
<table class="table table-bordered" style="width: 100%">
|
||||||
{% for d in doc.get(
|
{% for d in website_specifications -%}
|
||||||
{"doctype":"Item Website Specification"}) -%}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 30%;">{{ d.label }}</td>
|
<td style="width: 30%;">{{ d.label }}</td>
|
||||||
<td>{{ d.description }}</td>
|
<td>{{ d.description }}</td>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ frappe.ready(function() {
|
|||||||
type: "POST",
|
type: "POST",
|
||||||
method: "erpnext.shopping_cart.product.get_product_info",
|
method: "erpnext.shopping_cart.product.get_product_info",
|
||||||
args: {
|
args: {
|
||||||
item_code: "{{ name }}"
|
item_code: get_item_code()
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
$(".item-cart").toggleClass("hide", !!!r.message.price);
|
$(".item-cart").toggleClass("hide", !!!r.message.price);
|
||||||
@ -63,6 +63,15 @@ frappe.ready(function() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("[itemscope] .item-view-attribute select").on("change", function() {
|
||||||
|
var item_code = encodeURIComponent(get_item_code());
|
||||||
|
if (window.location.search.indexOf(item_code)!==-1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.load_via_ajax(window.location.pathname + "?variant=" + item_code);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var toggle_update_cart = function(qty) {
|
var toggle_update_cart = function(qty) {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ frappe.ui.form.on("Rename Tool", {
|
|||||||
method: "erpnext.utilities.doctype.rename_tool.rename_tool.get_doctypes",
|
method: "erpnext.utilities.doctype.rename_tool.rename_tool.get_doctypes",
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
frm.set_df_property("select_doctype", "options", r.message);
|
frm.set_df_property("select_doctype", "options", r.message);
|
||||||
frm.cscript.setup_upload();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -51,5 +51,6 @@ def upload(select_doctype=None, rows=None):
|
|||||||
rename_log.append("<span style='color: RED'>" + \
|
rename_log.append("<span style='color: RED'>" + \
|
||||||
_("Failed: ") + row[0] + " -> " + row[1] + "</span>")
|
_("Failed: ") + row[0] + " -> " + row[1] + "</span>")
|
||||||
rename_log.append("<span style='margin-left: 20px;'>" + repr(e) + "</span>")
|
rename_log.append("<span style='margin-left: 20px;'>" + repr(e) + "</span>")
|
||||||
|
frappe.db.rollback()
|
||||||
|
|
||||||
return rename_log
|
return rename_log
|
||||||
|
|||||||
2
setup.py
2
setup.py
@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
version = "6.5.1"
|
version = "6.5.2"
|
||||||
|
|
||||||
with open("requirements.txt", "r") as f:
|
with open("requirements.txt", "r") as f:
|
||||||
install_requires = f.readlines()
|
install_requires = f.readlines()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user