diff --git a/erpnext/selling/doctype/lead/test_records.json b/erpnext/selling/doctype/lead/test_records.json
index c286a12b28c..ce9460489cd 100644
--- a/erpnext/selling/doctype/lead/test_records.json
+++ b/erpnext/selling/doctype/lead/test_records.json
@@ -4,7 +4,7 @@
"email_id": "test_lead@example.com",
"lead_name": "_Test Lead",
"status": "Open",
- "territory": "_Test Territory Rest Of The World"
+ "territory": "_Test Territory"
},
{
"doctype": "Lead",
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 91a5efc1baa..fcdd4b2a01e 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -6,6 +6,7 @@ import frappe
from frappe.utils.nestedset import NestedSet
from frappe.website.website_generator import WebsiteGenerator
+from frappe.website.render import clear_cache
class ItemGroup(NestedSet, WebsiteGenerator):
nsm_parent_field = 'parent_item_group'
@@ -19,7 +20,9 @@ class ItemGroup(NestedSet, WebsiteGenerator):
self.parent_item_group)
def on_update(self):
- super(ItemGroup, self).on_update()
+ NestedSet.on_update(self)
+ WebsiteGenerator.on_update(self)
+ invalidate_cache_for(self)
self.validate_name_with_item()
self.validate_one_root()
@@ -32,3 +35,18 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate_name_with_item(self):
if frappe.db.exists("Item", self.name):
frappe.throw(frappe._("An item exists with same name ({0}), please change the item group name or rename the item").format(self.name))
+
+def get_parent_item_groups(item_group_name):
+ item_group = frappe.get_doc("Item Group", item_group_name)
+ return frappe.db.sql("""select name, page_name from `tabItem Group`
+ where lft <= %s and rgt >= %s
+ and ifnull(show_in_website,0)=1
+ order by lft asc""", (item_group.lft, item_group.rgt), as_dict=True)
+
+def invalidate_cache_for(doc, item_group=None):
+ if not item_group:
+ item_group = doc.name
+
+ for i in get_parent_item_groups(item_group):
+ if i.page_name:
+ clear_cache(i.page_name)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index b945309bdf7..26d827f8c95 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -6,6 +6,8 @@ import frappe
from frappe import msgprint, _
from frappe.utils import cstr, flt, getdate, now_datetime, formatdate
from frappe.website.website_generator import WebsiteGenerator
+from erpnext.setup.doctype.item_group.item_group import invalidate_cache_for
+from frappe.website.render import clear_cache
class WarehouseNotSet(frappe.ValidationError): pass
@@ -46,6 +48,7 @@ class Item(WebsiteGenerator):
def on_update(self):
super(Item, self).on_update()
+ invalidate_cache_for_item(self)
self.validate_name_with_item_group()
self.update_item_price()
@@ -230,6 +233,9 @@ class Item(WebsiteGenerator):
def after_rename(self, olddn, newdn, merge):
super(Item, self).after_rename(olddn, newdn, merge)
+ if self.page_name:
+ invalidate_cache_for_item(self)
+ clear_cache(self.page_name)
frappe.db.set_value("Item", newdn, "item_code", newdn)
if merge:
@@ -344,3 +350,8 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
})
return out
+
+def invalidate_cache_for_item(doc):
+ invalidate_cache_for(doc, doc.item_group)
+ for d in doc.get({"doctype":"Website Item Group"}):
+ invalidate_cache_for(doc, d.item_group)
diff --git a/erpnext/templates/generators/__init__.py b/erpnext/templates/generators/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/templates/generators/item.html b/erpnext/templates/generators/item.html
new file mode 100644
index 00000000000..4d4f5d44695
--- /dev/null
+++ b/erpnext/templates/generators/item.html
@@ -0,0 +1,84 @@
+{% block title %} {{ title }} {% endblock %}
+
+{% block header %}
{{ title }} {% endblock %}
+
+{% block content %}
+
+ {% include 'templates/includes/product_search_box.html' %}
+ {% include 'templates/includes/product_breadcrumbs.html' %}
+
+
+
+ {% if slideshow %}
+ {% include "templates/includes/slideshow.html" %}
+ {% else %}
+ {% if website_image %}
+
+ {% else %}
+
+ {% include 'templates/includes/product_missing_image.html' %}
+
+ {% endif %}
+ {% endif %}
+
+
+
{{ item_name }}
+
Item Code: {{ name }}
+
Product Description
+
+ {{ web_long_description or description or "[No description given]" }}
+
+
+
+
+
+
+
+ Add to Cart
+
+
+
+
+
+
+ {% if doc.get({"doctype":"Item Website Specification"}) -%}
+
+
+
Specifications
+
+ {% for d in doc.get(
+ {"doctype":"Item Website Specification"}) -%}
+
+ {{ d.label }}
+ {{ d.description }}
+
+ {%- endfor %}
+
+
+
+ {%- endif %}
+
+
+
+{% endblock %}
+
+{% block sidebar %}{% include "templates/includes/sidebar.html" %}{% endblock %}
diff --git a/erpnext/templates/generators/item.py b/erpnext/templates/generators/item.py
new file mode 100644
index 00000000000..1ad070ef87f
--- /dev/null
+++ b/erpnext/templates/generators/item.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from erpnext.setup.doctype.item_group.item_group import get_parent_item_groups
+from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
+
+doctype = "Item"
+condition_field = "show_in_website"
+
+def get_context(context):
+ item_context = context.doc.as_dict()
+ item_context["parent_groups"] = get_parent_item_groups(context.doc.item_group) + \
+ [{"name":context.doc.name}]
+ if context.doc.slideshow:
+ item_context.update(get_slideshow(context.doc))
+
+ return item_context
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
new file mode 100644
index 00000000000..ae0cd22c86d
--- /dev/null
+++ b/erpnext/templates/generators/item_group.html
@@ -0,0 +1,58 @@
+{% block title %} {{ title }} {% endblock %}
+
+{% block header %}{{ title }} {% endblock %}
+
+{% block content %}
+
+ {% include 'templates/includes/product_search_box.html' %}
+ {% include 'templates/includes/product_breadcrumbs.html' %}
+
+ {% if slideshow %}
+ {% include "templates/includes/slideshow.html" %}
+ {% endif %}
+ {% if description %}
+
{{ description or ""}}
+ {% else %}
+
{{ name }}
+ {% endif %}
+
+
+ {% if sub_groups %}
+
+
+ {% for d in sub_groups %}
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% if items %}
+
+ {% for item in items %}
+ {{ item }}
+ {% endfor %}
+
+ {% if (items|length)==100 %}
+
Showing top 100 items.
+ {% endif %}
+ {% else %}
+
No items listed.
+ {% endif %}
+
+
+
+
+{% endblock %}
+
+{% block sidebar %}{% include "templates/includes/sidebar.html" %}{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/generators/item_group.py b/erpnext/templates/generators/item_group.py
new file mode 100644
index 00000000000..774301888d1
--- /dev/null
+++ b/erpnext/templates/generators/item_group.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
+from erpnext.setup.doctype.item_group.item_group import get_parent_item_groups
+
+doctype = "Item Group"
+condition_field = "show_in_website"
+
+def get_context(context):
+ item_group_context = context.doc.as_dict()
+ item_group_context.update({
+ "sub_groups": frappe.db.sql("""select name, page_name
+ from `tabItem Group` where parent_item_group=%s
+ and ifnull(show_in_website,0)=1""", context.docname, as_dict=1),
+ "items": get_product_list_for_group(product_group = context.docname, limit=100),
+ "parent_groups": get_parent_item_groups(context.docname),
+ "title": context.docname
+ })
+
+ if context.doc.slideshow:
+ item_group_context.update(get_slideshow(context.doc))
+
+ for d in item_group_context.sub_groups:
+ d.count = get_group_item_count(d.name)
+
+ return item_group_context
+
+def get_product_list_for_group(product_group=None, start=0, limit=10):
+ child_groups = ", ".join(['"' + i[0] + '"' for i in get_child_groups(product_group)])
+
+ # base query
+ query = """select name, item_name, page_name, website_image, item_group,
+ web_long_description as website_description
+ from `tabItem` where docstatus = 0 and show_in_website = 1
+ and (item_group in (%s)
+ or name in (select parent from `tabWebsite Item Group` where item_group in (%s))) """ % (child_groups, child_groups)
+
+ query += """order by weightage desc, modified desc limit %s, %s""" % (start, limit)
+
+ data = frappe.db.sql(query, {"product_group": product_group}, as_dict=1)
+
+ return [get_item_for_list_in_html(r) for r in data]
+
+def get_child_groups(item_group_name):
+ item_group = frappe.get_doc("Item Group", item_group_name)
+ return frappe.db.sql("""select name
+ from `tabItem Group` where lft>=%(lft)s and rgt<=%(rgt)s
+ and show_in_website = 1""", item_group.as_dict())
+
+def get_item_for_list_in_html(context):
+ return frappe.get_template("templates/includes/product_in_grid.html").render(context)
+
+def get_group_item_count(item_group):
+ child_groups = ", ".join(['"' + i[0] + '"' for i in get_child_groups(item_group)])
+ return frappe.db.sql("""select count(*) from `tabItem`
+ where docstatus = 0 and show_in_website = 1
+ and (item_group in (%s)
+ or name in (select parent from `tabWebsite Item Group`
+ where item_group in (%s))) """ % (child_groups, child_groups))[0][0]
+
diff --git a/erpnext/templates/generators/partner.html b/erpnext/templates/generators/partner.html
new file mode 100644
index 00000000000..6a869a12074
--- /dev/null
+++ b/erpnext/templates/generators/partner.html
@@ -0,0 +1,29 @@
+{% block title %} {{ title }} {% endblock %}
+
+{% block header %}{{ title }} {% endblock %}
+
+{% block content %}
+
+
+
+ {% if logo -%}
+
+
+ {%- endif %}
+
+ {% if partner_website -%}{{ partner_website }}
{%- endif %}
+ {% if partner_address -%}{{ partner_address }}
{%- endif %}
+ {% if phone -%}{{ phone }}
{%- endif %}
+ {% if email -%} {{ email }}
{%- endif %}
+
+
+
+
+
+{% endblock %}
+
+{% block sidebar %}{% include "templates/includes/sidebar.html" %}{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/generators/partner.py b/erpnext/templates/generators/partner.py
new file mode 100644
index 00000000000..20793097476
--- /dev/null
+++ b/erpnext/templates/generators/partner.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import filter_strip_join
+
+doctype = "Sales Partner"
+condition_field = "show_in_website"
+
+def get_context(context):
+ partner_context = context.doc.as_dict()
+
+ address = frappe.db.get_value("Address",
+ {"sales_partner": context.doc.name, "is_primary_address": 1},
+ "*", as_dict=True)
+ if address:
+ city_state = ", ".join(filter(None, [address.city, address.state]))
+ address_rows = [address.address_line1, address.address_line2,
+ city_state, address.pincode, address.country]
+
+ partner_context.update({
+ "email": address.email_id,
+ "partner_address": filter_strip_join(address_rows, "\n "),
+ "phone": filter_strip_join(cstr(address.phone).split(","), "\n ")
+ })
+
+ return partner_context
diff --git a/erpnext/templates/includes/__init__.py b/erpnext/templates/includes/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/templates/includes/product_breadcrumbs.html b/erpnext/templates/includes/product_breadcrumbs.html
new file mode 100644
index 00000000000..b126847ff1f
--- /dev/null
+++ b/erpnext/templates/includes/product_breadcrumbs.html
@@ -0,0 +1,10 @@
+{% if parent_groups and (parent_groups|length) > 1 %}
+
+
+ {% for ig in parent_groups[:-1] %}
+ {{ ig.name }}
+ {% endfor %}
+ {{ parent_groups[-1].name }}
+
+
+{% endif %}
\ No newline at end of file
diff --git a/erpnext/templates/includes/product_in_grid.html b/erpnext/templates/includes/product_in_grid.html
new file mode 100644
index 00000000000..9ef4bbd9b12
--- /dev/null
+++ b/erpnext/templates/includes/product_in_grid.html
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/erpnext/templates/includes/product_in_list.html b/erpnext/templates/includes/product_in_list.html
new file mode 100644
index 00000000000..116124a78b2
--- /dev/null
+++ b/erpnext/templates/includes/product_in_list.html
@@ -0,0 +1,15 @@
+
+
\ No newline at end of file
diff --git a/erpnext/templates/includes/product_list.js b/erpnext/templates/includes/product_list.js
new file mode 100644
index 00000000000..3d19d5a0c10
--- /dev/null
+++ b/erpnext/templates/includes/product_list.js
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+window.get_product_list = function() {
+ $(".more-btn .btn").click(function() {
+ window.get_product_list()
+ });
+
+ if(window.start==undefined) {
+ throw "product list not initialized (no start)"
+ }
+
+ $.ajax({
+ method: "GET",
+ url: "/",
+ dataType: "json",
+ data: {
+ cmd: "erpnext.templates.pages.product_search.get_product_list",
+ start: window.start,
+ search: window.search,
+ product_group: window.product_group
+ },
+ dataType: "json",
+ success: function(data) {
+ window.render_product_list(data.message);
+ }
+ })
+}
+
+window.render_product_list = function(data) {
+ if(data.length) {
+ var table = $("#search-list .table");
+ if(!table.length)
+ var table = $("").appendTo("#search-list");
+
+ $.each(data, function(i, d) {
+ $(d).appendTo(table);
+ });
+ }
+ if(data.length < 10) {
+ if(!table) {
+ $(".more-btn")
+ .replaceWith("No products found.
");
+ } else {
+ $(".more-btn")
+ .replaceWith("Nothing more to show.
");
+ }
+ } else {
+ $(".more-btn").toggle(true)
+ }
+ window.start += (data.length || 0);
+}
diff --git a/erpnext/templates/includes/product_missing_image.html b/erpnext/templates/includes/product_missing_image.html
new file mode 100644
index 00000000000..81b893533f9
--- /dev/null
+++ b/erpnext/templates/includes/product_missing_image.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js
new file mode 100644
index 00000000000..42d4ae7ca58
--- /dev/null
+++ b/erpnext/templates/includes/product_page.js
@@ -0,0 +1,78 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+// License: GNU General Public License v3. See license.txt
+
+$(document).ready(function() {
+ var item_code = $('[itemscope] [itemprop="productID"]').text().trim();
+ var qty = 0;
+
+ frappe.call({
+ type: "POST",
+ method: "shopping_cart.shopping_cart.product.get_product_info",
+ args: {
+ item_code: "{{ name }}"
+ },
+ callback: function(r) {
+ if(r.message && r.message.price) {
+ $(".item-price")
+ .html(r.message.price.formatted_price + " per " + r.message.uom);
+
+ if(r.message.stock==0) {
+ $(".item-stock").html("Not in stock
");
+ }
+ else if(r.message.stock==1) {
+ $(".item-stock").html("\
+ Available (in stock)
");
+ }
+
+ $(".item-price-info").toggle(true);
+
+ if(r.message.qty) {
+ qty = r.message.qty;
+ toggle_update_cart(qty);
+ $("#item-update-cart input").val(qty);
+ }
+ }
+ }
+ })
+
+ $("#item-add-to-cart button").on("click", function() {
+ shopping_cart.update_cart({
+ item_code: item_code,
+ qty: 1,
+ callback: function(r) {
+ if(!r.exc) {
+ toggle_update_cart(1);
+ qty = 1;
+ }
+ },
+ btn: this,
+ });
+ });
+
+ $("#item-update-cart button").on("click", function() {
+ shopping_cart.update_cart({
+ item_code: item_code,
+ qty: $("#item-update-cart input").val(),
+ btn: this,
+ callback: function(r) {
+ if(r.exc) {
+ $("#item-update-cart input").val(qty);
+ } else {
+ qty = $("#item-update-cart input").val();
+ }
+ },
+ });
+ });
+
+ if(localStorage && localStorage.getItem("pending_add_to_cart") && full_name) {
+ localStorage.removeItem("pending_add_to_cart");
+ $("#item-add-to-cart button").trigger("click");
+ }
+});
+
+var toggle_update_cart = function(qty) {
+ $("#item-add-to-cart").toggle(qty ? false : true);
+ $("#item-update-cart")
+ .toggle(qty ? true : false)
+ .find("input").val(qty);
+}
\ No newline at end of file
diff --git a/erpnext/templates/includes/product_search_box.html b/erpnext/templates/includes/product_search_box.html
new file mode 100644
index 00000000000..0f44eea72e6
--- /dev/null
+++ b/erpnext/templates/includes/product_search_box.html
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/erpnext/templates/pages/__init__.py b/erpnext/templates/pages/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/erpnext/templates/pages/partners.html b/erpnext/templates/pages/partners.html
new file mode 100644
index 00000000000..089c15b68fc
--- /dev/null
+++ b/erpnext/templates/pages/partners.html
@@ -0,0 +1,30 @@
+{% block title %} {{ title }} {% endblock %}
+
+{% block header %}{{ title }} {% endblock %}
+
+{% block content %}
+
+ {% for partner_info in partners %}
+
+
+ {% if partner_info.logo -%}
+
+
+
+ {%- endif %}
+
+
+
+
+ {% endfor %}
+
+{% endblock %}
+
+{% block sidebar %}{% include "templates/includes/sidebar.html" %}{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/partners.py b/erpnext/templates/pages/partners.py
new file mode 100644
index 00000000000..94b553c2b2a
--- /dev/null
+++ b/erpnext/templates/pages/partners.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+import frappe.website.render
+
+def get_context(context):
+ return {
+ "partners": frappe.db.sql("""select * from `tabSales Partner`
+ where show_in_website=1 order by name asc""", as_dict=True),
+ "title": "Partners"
+ }
diff --git a/erpnext/templates/pages/product_search.html b/erpnext/templates/pages/product_search.html
new file mode 100644
index 00000000000..8e57cb5c0e8
--- /dev/null
+++ b/erpnext/templates/pages/product_search.html
@@ -0,0 +1,33 @@
+{% block title %} {{ "Product Search" }} {% endblock %}
+
+{% block header %}Product Search {% endblock %}
+
+{% block content %}
+
+
+
+
+{% include "templates/includes/product_search_box.html" %}
+
+
Search Results
+
+
+
+
+
+{% endblock %}
+
+{% block sidebar %}{% include "templates/includes/sidebar.html" %}{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py
new file mode 100644
index 00000000000..8d4a8f6425e
--- /dev/null
+++ b/erpnext/templates/pages/product_search.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import cstr
+from erpnext.templates.generators.item_group import get_item_for_list_in_html
+
+no_cache = 1
+no_sitemap = 1
+
+@frappe.whitelist(allow_guest=True)
+def get_product_list(search=None, start=0, limit=10):
+ # base query
+ query = """select name, item_name, page_name, website_image, item_group,
+ web_long_description as website_description
+ from `tabItem` where docstatus = 0 and show_in_website = 1 """
+
+ # search term condition
+ if search:
+ query += """and (web_long_description like %(search)s or
+ item_name like %(search)s or name like %(search)s)"""
+ search = "%" + cstr(search) + "%"
+
+ # order by
+ query += """order by weightage desc, modified desc limit %s, %s""" % (start, limit)
+
+ data = frappe.db.sql(query, {
+ "search": search,
+ }, as_dict=1)
+
+ return [get_item_for_list_in_html(r) for r in data]
+
diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py
index e98a5c046b5..7ba4a9d911e 100644
--- a/erpnext/templates/utils.py
+++ b/erpnext/templates/utils.py
@@ -7,10 +7,10 @@ import frappe
@frappe.whitelist(allow_guest=True)
def send_message(subject="Website Query", message="", sender="", status="Open"):
from frappe.templates.pages.contact import send_message as website_send_message
-
+
if not website_send_message(subject, message, sender):
return
-
+
if subject=="Support":
# create support ticket
from erpnext.support.doctype.support_ticket.get_support_mails import add_support_communication
@@ -18,6 +18,6 @@ def send_message(subject="Website Query", message="", sender="", status="Open"):
else:
# make lead / communication
from erpnext.selling.doctype.lead.get_leads import add_sales_communication
- add_sales_communication(subject or "Website Query", message, sender, sender,
+ add_sales_communication(subject or "Website Query", message, sender, sender,
mail=None, status=status)
-
\ No newline at end of file
+