[hub] start

This commit is contained in:
Rushabh Mehta 2015-02-18 19:08:57 +05:30
parent 9252002249
commit 4ea1ade7b9
17 changed files with 1355 additions and 767 deletions

View File

@ -0,0 +1,17 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, requests
@frappe.whitelist()
def get_items(text, start, limit):
hub = frappe.get_single("Hub Settings")
response = requests.get(hub.hub_url + "/api/method/hub.hub.api.get_items", params={
"access_token": hub.access_token,
"text": text,
"start": start,
"limit": limit
})
response.raise_for_status()
return response.json().get("message")

View File

View File

@ -0,0 +1,17 @@
frappe.ui.form.on("Hub Settings", "onload", function(frm) {
if(!frm.doc.seller_country) {
frm.set_value("seller_country", frappe.defaults.get_default("country"));
}
if(!frm.doc.seller_name) {
frm.set_value("seller_name", frappe.defaults.get_default("company"));
}
});
frappe.ui.form.on("Hub Settings", "refresh", function(frm) {
// make mandatory if published
frm.toggle_reqd(["seller_name", "seller_email", "seller_country"], frm.doc.publish);
});
frappe.ui.form.on("Hub Settings", "publish", function(frm) {
frm.trigger("refresh");
});

View File

@ -0,0 +1,268 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"creation": "2015-02-18 00:59:34.560476",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "publish",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Publish Items to Hub",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"depends_on": "publish",
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "publish_pricing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Publish Pricing",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "publish_availability",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Publish Availability",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "sync_now",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sync Now",
"no_copy": 0,
"options": "sync",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"depends_on": "publish",
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "seller_name",
"fieldtype": "Data",
"label": "Seller Name",
"permlevel": 0,
"precision": "",
"reqd": 0
},
{
"allow_on_submit": 0,
"fieldname": "seller_country",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Seller Country",
"no_copy": 0,
"options": "Country",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"fieldname": "seller_email",
"fieldtype": "Data",
"label": "Seller Email",
"options": "Email",
"permlevel": 0,
"precision": "",
"reqd": 0
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "seller_city",
"fieldtype": "Data",
"label": "Seller City",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "seller_website",
"fieldtype": "Data",
"label": "Seller Website",
"permlevel": 0,
"precision": ""
},
{
"depends_on": "publish",
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "seller_description",
"fieldtype": "Text Editor",
"label": "Seller Description",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "name_token",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Name Token",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "access_token",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Access Token",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-02-18 08:14:46.140473",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "Hub Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,96 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, requests, json
from frappe.model.document import Document
from frappe.utils import cint, expand_relative_urls
from frappe import _
class HubSettings(Document):
hub_url = "http://localhost:8001"
def validate(self):
if cint(self.publish):
if not self.name_token:
self.register()
else:
self.update_seller_details()
self.publish_selling_items()
else:
if self.name_token:
self.unpublish()
def publish_selling_items(self):
"""Set `publish_in_hub`=1 for all Sales Items"""
for item in frappe.get_all("Item", fields=["name"],
filters={"is_sales_item": "Yes", "publish_in_hub": "0"}):
frappe.db.set_value("Item", item.name, "publish_in_hub", 1)
def register(self):
"""Register at hub.erpnext.com, save `name_token` and `access_token`"""
response = requests.post(self.hub_url + "/api/method/hub.hub.api.register", data=self.get_args())
response.raise_for_status()
response = response.json()
self.name_token = response.get("message").get("name")
self.access_token = response.get("message").get("access_token")
def unpublish(self):
"""Unpublish from hub.erpnext.com"""
response = requests.post(self.hub_url + "/api/method/hub.hub.api.unpublish", data={
"access_token": self.access_token
})
response.raise_for_status()
def update_seller_details(self):
"""Update details at hub.erpnext.com"""
args = self.get_args()
args["published"] = 1
response = requests.post(self.hub_url + "/api/method/hub.hub.api.update_seller", data={
"access_token": self.access_token,
"args": json.dumps(args)
})
response.raise_for_status()
def get_args(self):
return {
"seller_name": self.seller_name,
"seller_country": self.seller_country,
"seller_city": self.seller_city,
"seller_email": self.seller_email,
"seller_website": self.seller_website,
"seller_description": self.seller_description
}
def sync(self, verbose=True):
"""Sync items with hub.erpnext.com"""
if not self.publish:
if verbose:
frappe.msgprint(_("Publish to sync items"))
return
items = frappe.db.get_all("Item",
fields=["name", "item_name", "description", "image", "item_group"],
filters={"publish_in_hub": 1, "synced_with_hub": 0})
for item in items:
item.item_code = item.name
if item.image:
item.image = expand_relative_urls(item.image)
item_list = frappe.db.sql_list("select name from tabItem where ifnull(publish_in_hub,0)=1")
if items:
response = requests.post(self.hub_url + "/api/method/hub.hub.api.sync", data={
"access_token": self.access_token,
"items": json.dumps(items),
"item_list": json.dumps(item_list)
})
response.raise_for_status()
for item in items:
frappe.db.set_value("Item", item.name, "synced_with_hub", 1)
if verbose:
frappe.msgprint(_("{0} Items synced".format(len(items))))
else:
if verbose:
frappe.msgprint(_("Items already synced"))

View File

View File

View File

@ -0,0 +1,90 @@
frappe.pages['hub'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Hub',
single_column: true
});
frappe.hub = new frappe.Hub({page:page});
}
frappe.pages['hub'].on_page_show = function() {
frappe.hub.refresh();
}
frappe.Hub = Class.extend({
init: function(args) {
$.extend(this, args);
this.render();
},
refresh: function() {
if(this.hub && this.hub.publish && !this.hub_list) {
this.setup_list();
}
},
render: function() {
this.page.main.empty();
var me = this;
frappe.model.with_doc("Hub Settings", "Hub Settings", function() {
me.hub = locals["Hub Settings"]["Hub Settings"];
if(!me.hub.publish) {
$(frappe.render_template("register_in_hub", {})).appendTo(me.page.main);
} else {
me.setup_list();
}
});
},
setup_list: function() {
var me = this;
$(frappe.render_template("hub_body", {})).appendTo(this.page.main);
this.hub_list = this.page.main.find(".hub-list");
this.search = this.page.main.find("input").on("keypress", function(e) {
if(e.which===13) {
me.reset();
}
});
this.loading = this.page.main.find(".loading");
this.done = this.page.main.find(".done");
this.more = this.page.main.find(".more")
this.more.find(".btn").on("click", function() { me.next_page() });
this.reset();
},
reset: function() {
this.hub_list.empty();
this.start = 0;
this.page_length = 20;
this.next_page();
},
next_page: function() {
var me = this;
this.loading.toggleClass("hide", false);
frappe.call({
method: "erpnext.hub_node.get_items",
args: {
text: this.get_text(),
start: this.start,
limit: this.page_length
},
callback: function(r) {
me.loading.toggleClass("hide", true);
if(!r.message)
r.message = [];
me.start += r.message.length;
$(frappe.render_template("hub_list", {items: r.message})).appendTo(me.hub_list);
if(r.message.length && r.message.length===me.page_length) {
// more
me.more.removeClass("hide");
me.done.addClass("hide");
} else {
// done
me.more.addClass("hide");
me.done.removeClass("hide");
}
}
});
},
get_text: function() {
return this.search.val();
},
})

View File

@ -0,0 +1,21 @@
{
"content": null,
"creation": "2015-02-18 05:17:17.301735",
"docstatus": 0,
"doctype": "Page",
"modified": "2015-02-18 05:17:17.301735",
"modified_by": "Administrator",
"module": "Hub Node",
"name": "hub",
"owner": "Administrator",
"page_name": "hub",
"roles": [
{
"role": "All"
}
],
"script": null,
"standard": "Yes",
"style": null,
"title": "Hub"
}

View File

@ -0,0 +1,20 @@
<div class="padding">
<div class="row">
<div class="col-md-4 col-md-offset-4">
<input class="form-control" name="search" placeholder="{%= __("Search") %}">
</div>
</div>
<hr style="margin: 15px -15px">
<div class="hub-list">
</div>
<div class="loading">
<p class="text-muted text-center">{%= __("Loading...") %}</p>
</div>
<div class="more text-center hide">
<button class="btn btn-default btn-sm">{%= __("More") %}</div>
</div>
<div class="done text-center text-extra-muted hide">
<p>{%= __("No more results.") %}</p>
</div>
</div>

View File

@ -0,0 +1,16 @@
{% for(var i=0, l=items.length; i < l; i++) { %}
<div class="list-item">
<div class="row">
<div class="col-sm-6">
<h6>{%= items[i].item_name %}<h6>
</div>
<div class="col-sm-3">
<h6>{%= items[i].seller_name %}<h6>
</div>
<div class="col-sm-3 text-right">
<h6 class="text-muted">{%= items[i].seller_country %}<h6>
</div>
</div>
<p class="text-muted small">{%= items[i].description %}</p>
</div>
{% } %}

View File

@ -0,0 +1,19 @@
<div style="padding: 70px 0px;">
<h2 class="text-center">{%= __("Register For ERPNext Hub") %}</h2>
<br>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<ul>
<li>Free listing of your products</li>
<li>Let other ERPNext users discover your products</li>
<li>Discover products quickly</li>
<li>Automate workflow with Supplier from within ERPNext (later)</li>
</ul>
</div>
</div>
<br>
<div class="text-center">
<a class="btn btn-primary" href="#Form/Hub Settings">
Hub Settings</a>
</div>
</div>

View File

@ -11,3 +11,4 @@ Support
Utilities
Contacts
Shopping Cart
Hub Node

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,10 @@ class Item(WebsiteGenerator):
self.name = self.item_code
def before_insert(self):
if self.is_sales_item=="Yes":
self.publish_in_hub = 1
def validate(self):
super(Item, self).validate()
@ -58,6 +62,7 @@ class Item(WebsiteGenerator):
self.validate_warehouse_for_reorder()
self.validate_variants()
self.update_item_desc()
self.synced_with_hub = 0
if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
@ -434,13 +439,13 @@ class Item(WebsiteGenerator):
row = self.append("website_specifications")
row.label = label
row.description = desc
def update_item_desc(self):
if frappe.db.get_value('BOM',self.name, 'description') != self.description:
frappe.db.sql("""update `tabBOM` set description = %s where item = %s and docstatus < 2""",(self.description, self.name))
frappe.db.sql("""update `tabBOM Item` set description = %s where item_code = %s and docstatus < 2""",(self.description, self.name))
frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where item_code = %s and docstatus < 2""",(self.description, self.name))
frappe.db.sql("""update `tabBOM Explosion Item` set description = %s where item_code = %s and docstatus < 2""",(self.description, self.name))
def validate_end_of_life(item_code, end_of_life=None, verbose=1):
if not end_of_life:
end_of_life = frappe.db.get_value("Item", item_code, "end_of_life")

View File

@ -35,13 +35,6 @@
"fieldtype": "Column Break",
"permlevel": 0
},
{
"fieldname": "allow_negative_stock",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Allow Negative Stock",
"permlevel": 0
},
{
"fieldname": "valuation_method",
"fieldtype": "Select",
@ -56,6 +49,13 @@
"label": "Allowance Percent",
"permlevel": 0
},
{
"fieldname": "allow_negative_stock",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Allow Negative Stock",
"permlevel": 0
},
{
"fieldname": "auto_material_request",
"fieldtype": "Section Break",
@ -103,7 +103,7 @@
"icon": "icon-cog",
"idx": 1,
"issingle": 1,
"modified": "2015-02-05 05:11:47.229371",
"modified": "2015-02-18 08:37:18.229705",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",