diff --git a/erpnext/demo/data/drug_list.json b/erpnext/demo/data/drug_list.json index f34ca572d23..9b101cb1c81 100644 --- a/erpnext/demo/data/drug_list.json +++ b/erpnext/demo/data/drug_list.json @@ -48,7 +48,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -133,7 +132,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -218,7 +216,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -303,7 +300,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -388,7 +384,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -473,7 +468,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -558,7 +552,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -643,7 +636,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -728,7 +720,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -813,7 +804,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -898,7 +888,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -983,7 +972,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1068,7 +1056,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1153,7 +1140,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1238,7 +1224,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1323,7 +1308,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1408,7 +1392,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1493,7 +1476,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1578,7 +1560,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1663,7 +1644,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1748,7 +1728,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1833,7 +1812,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -1918,7 +1896,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2003,7 +1980,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2088,7 +2064,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2173,7 +2148,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2258,7 +2232,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2343,7 +2316,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2428,7 +2400,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2513,7 +2484,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2598,7 +2568,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2683,7 +2652,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2768,7 +2736,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2853,7 +2820,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -2938,7 +2904,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3023,7 +2988,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3108,7 +3072,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3193,7 +3156,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3278,7 +3240,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3363,7 +3324,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3448,7 +3408,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3533,7 +3492,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3618,7 +3576,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3703,7 +3660,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3788,7 +3744,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3873,7 +3828,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -3958,7 +3912,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4043,7 +3996,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4128,7 +4080,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4213,7 +4164,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4298,7 +4248,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4383,7 +4332,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4468,7 +4416,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4553,7 +4500,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4638,7 +4584,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4723,7 +4668,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4808,7 +4752,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4893,7 +4836,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -4978,7 +4920,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -5063,7 +5004,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -5148,7 +5088,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -5233,7 +5172,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, @@ -5318,7 +5256,6 @@ "naming_series": null, "net_weight": 0.0, "opening_stock": 0.0, - "publish_in_hub": 1, "quality_parameters": [], "reorder_levels": [], "route": null, diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index 65b1386466f..634746caf22 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -2,10 +2,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, requests, json -from frappe.utils import now, nowdate, cint -from frappe.utils.nestedset import get_root_of -from frappe.contacts.doctype.contact.contact import get_default_contact +import frappe @frappe.whitelist() def enable_hub(): @@ -13,265 +10,3 @@ def enable_hub(): hub_settings.register() frappe.db.commit() return hub_settings - -@frappe.whitelist() -def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=None): - connection = get_client_connection() - filters = json.loads(filters) - - response = connection.get_list(doctype, - limit_start=start, limit_page_length=limit, - filters=filters, fields=fields) - - # Bad, need child tables in response - listing = [] - for obj in response: - doc = connection.get_doc(doctype, obj['name']) - listing.append(doc) - - return listing - -@frappe.whitelist() -def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None): - doctype = 'Hub Item' - hub_settings = frappe.get_doc('Hub Settings') - item_names_str = hub_settings.get('custom_data') or '[]' - item_names = json.loads(item_names_str) - filters = json.dumps({ - 'hub_item_code': ['in', item_names] - }) - return get_list(doctype, start, limit, fields, filters, order_by) - -@frappe.whitelist() -def update_wishlist_item(item_name, remove=0): - remove = int(remove) - hub_settings = frappe.get_doc('Hub Settings') - data = hub_settings.get('custom_data') - if not data or not json.loads(data): - data = '[]' - hub_settings.custom_data = data - hub_settings.save() - - item_names_str = data - item_names = json.loads(item_names_str) - if not remove and item_name not in item_names: - item_names.append(item_name) - if remove and item_name in item_names: - item_names.remove(item_name) - - item_names_str = json.dumps(item_names) - - hub_settings.custom_data = item_names_str - hub_settings.save() - -@frappe.whitelist() -def get_meta(doctype): - connection = get_client_connection() - meta = connection.get_doc('DocType', doctype) - categories = connection.get_list('Hub Category', - limit_start=0, limit_page_length=300, - filters={}, fields=['name']) - - categories = [d.get('name') for d in categories] - return { - 'meta': meta, - 'companies': connection.get_list('Hub Company', - limit_start=0, limit_page_length=300, - filters={}, fields=['name']), - 'categories': categories - } - -@frappe.whitelist() -def get_categories(parent='All Categories'): - # get categories info with parent category and stuff - connection = get_client_connection() - categories = connection.get_list('Hub Category', filters={'parent_hub_category': parent}) - - response = [{'value': c.get('name'), 'expandable': c.get('is_group')} for c in categories] - return response - -@frappe.whitelist() -def update_category(hub_item_code, category): - connection = get_hub_connection() - - # args = frappe._dict(dict( - # doctype='Hub Category', - # hub_category_name=category - # )) - # response = connection.insert('Hub Category', args) - - response = connection.update('Hub Item', frappe._dict(dict( - doctype='Hub Item', - hub_category = category - )), hub_item_code) - - return response - -@frappe.whitelist() -def send_review(hub_item_code, review): - review = json.loads(review) - hub_connection = get_hub_connection() - - item_doc = hub_connection.connection.get_doc('Hub Item', hub_item_code) - existing_reviews = item_doc.get('reviews') - - reviews = [review] - review.setdefault('idx', 0) - for r in existing_reviews: - if r.get('user') != review.get('user'): - reviews.append(r) - - response = hub_connection.update('Hub Item', dict( - doctype='Hub Item', - reviews = reviews - ), hub_item_code) - - return response - -@frappe.whitelist() -def get_details(hub_sync_id=None, doctype='Hub Item'): - if not hub_sync_id: - return - connection = get_client_connection() - details = connection.get_doc(doctype, hub_sync_id) - reviews = details.get('reviews') - if reviews and len(reviews): - for r in reviews: - r.setdefault('pretty_date', frappe.utils.pretty_date(r.get('modified'))) - details.setdefault('reviews', reviews) - return details - -def get_client_connection(): - # frappeclient connection - hub_connection = get_hub_connection() - return hub_connection.connection - -def get_hub_connection(): - hub_connector = frappe.get_doc( - 'Data Migration Connector', 'Hub Connector') - hub_connection = hub_connector.get_connection() - return hub_connection - -def make_opportunity(buyer_name, email_id): - buyer_name = "HUB-" + buyer_name - - if not frappe.db.exists('Lead', {'email_id': email_id}): - lead = frappe.new_doc("Lead") - lead.lead_name = buyer_name - lead.email_id = email_id - lead.save(ignore_permissions=True) - - o = frappe.new_doc("Opportunity") - o.enquiry_from = "Lead" - o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"] - o.save(ignore_permissions=True) - -@frappe.whitelist() -def make_rfq_and_send_opportunity(item, supplier): - supplier = make_supplier(supplier) - contact = make_contact(supplier) - item = make_item(item) - rfq = make_rfq(item, supplier, contact) - status = send_opportunity(contact) - - return { - 'rfq': rfq, - 'hub_document_created': status - } - -def make_supplier(supplier): - # make supplier if not already exists - supplier = frappe._dict(json.loads(supplier)) - - if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}): - supplier_doc = frappe.get_doc({ - 'doctype': 'Supplier', - 'supplier_name': supplier.supplier_name, - 'supplier_group': supplier.supplier_group, - 'supplier_email': supplier.supplier_email - }).insert() - else: - supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name) - - return supplier_doc - -def make_contact(supplier): - contact_name = get_default_contact('Supplier', supplier.supplier_name) - # make contact if not already exists - if not contact_name: - contact = frappe.get_doc({ - 'doctype': 'Contact', - 'first_name': supplier.supplier_name, - 'email_id': supplier.supplier_email, - 'is_primary_contact': 1, - 'links': [ - {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} - ] - }).insert() - else: - contact = frappe.get_doc('Contact', contact_name) - - return contact - -def make_item(item): - # make item if not already exists - item = frappe._dict(json.loads(item)) - - if not frappe.db.exists('Item', {'item_code': item.item_code}): - item_doc = frappe.get_doc({ - 'doctype': 'Item', - 'item_code': item.item_code, - 'item_group': item.item_group, - 'is_item_from_hub': 1 - }).insert() - else: - item_doc = frappe.get_doc('Item', item.item_code) - - return item_doc - -def make_rfq(item, supplier, contact): - # make rfq - rfq = frappe.get_doc({ - 'doctype': 'Request for Quotation', - 'transaction_date': nowdate(), - 'status': 'Draft', - 'company': frappe.db.get_single_value('Hub Settings', 'company'), - 'message_for_supplier': 'Please supply the specified items at the best possible rates', - 'suppliers': [ - { 'supplier': supplier.name, 'contact': contact.name } - ], - 'items': [ - { - 'item_code': item.item_code, - 'qty': 1, - 'schedule_date': nowdate(), - 'warehouse': item.default_warehouse or get_root_of("Warehouse"), - 'description': item.description, - 'uom': item.stock_uom - } - ] - }).insert() - - rfq.save() - rfq.submit() - return rfq - -def send_opportunity(contact): - # Make Hub Message on Hub with lead data - doc = { - 'doctype': 'Lead', - 'lead_name': frappe.db.get_single_value('Hub Settings', 'company'), - 'email_id': frappe.db.get_single_value('Hub Settings', 'user') - } - - args = frappe._dict(dict( - doctype='Hub Message', - reference_doctype='Lead', - data=json.dumps(doc), - user=contact.email_id - )) - - connection = get_hub_connection() - response = connection.insert('Hub Message', args) - - return response.ok diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py new file mode 100644 index 00000000000..559e22a2c8d --- /dev/null +++ b/erpnext/hub_node/api.py @@ -0,0 +1,100 @@ +from __future__ import unicode_literals +import frappe, requests, json +from frappe.utils import now +from frappe.frappeclient import FrappeClient + +@frappe.whitelist() +def call_hub_method(method, params=None): + connection = get_hub_connection() + + if type(params) == unicode: + params = json.loads(params) + + params.update({ + 'cmd': 'hub.hub.api.' + method + }) + + response = connection.post_request(params) + return response + +@frappe.whitelist() +def get_valid_items(search_value=''): + items = frappe.get_list( + 'Item', + fields=["*"], + filters={ + 'item_name': ['like', '%' + search_value + '%'], + 'publish_in_hub': 0 + }, + order_by="modified desc" + ) + + valid_items = filter(lambda x: x.image and x.description, items) + + def attach_source_type(item): + item.source_type = "local" + return item + + valid_items = map(lambda x: attach_source_type(x), valid_items) + return valid_items + +@frappe.whitelist() +def publish_selected_items(items_to_publish): + items_to_publish = json.loads(items_to_publish) + if not len(items_to_publish): + return + + for item_code in items_to_publish: + frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) + + try: + hub_settings = frappe.get_doc('Hub Settings') + item_sync_preprocess() + hub_settings.sync() + except Exception as e: + frappe.db.set_value("Hub Settings", "Hub Settings", "sync_in_progress", 0) + frappe.throw(e) + +def item_sync_preprocess(): + hub_seller = frappe.db.get_value("Hub Settings", "Hub Settings", "company_email") + + response = call_hub_method('add_hub_seller_activity', { + 'hub_seller': hub_seller, + 'activity_details': json.dumps({ + 'subject': 'Publishing items', + 'status': 'Success' + }) + }) + + if response: + frappe.db.set_value("Hub Settings", "Hub Settings", "sync_in_progress", 1) + return response + else: + frappe.throw('Unable to update remote activity') + +def item_sync_postprocess(sync_details): + hub_seller = frappe.db.get_value("Hub Settings", "Hub Settings", "company_email") + + response = call_hub_method('add_hub_seller_activity', { + 'hub_seller': hub_seller, + 'activity_details': json.dumps({ + 'subject': 'Publishing items:' + sync_details['status'], + 'content': json.dumps(sync_details['stats']) + }) + }) + + if response: + frappe.db.set_value('Hub Settings', 'Hub Settings', 'sync_in_progress', 0) + frappe.db.set_value('Hub Settings', 'Hub Settings', 'last_sync_datetime', frappe.utils.now()) + else: + frappe.throw('Unable to update remote activity') + +def get_hub_connection(): + if frappe.db.exists('Data Migration Connector', 'Hub Connector'): + hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector') + hub_connection = hub_connector.get_connection() + return hub_connection.connection + + # read-only connection + hub_connection = FrappeClient(frappe.conf.hub_url) + return hub_connection diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py index e69de29bb2d..9445e3a8dc5 100644 --- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py +++ b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py @@ -0,0 +1,19 @@ +import io, base64, urllib, os + +def pre_process(doc): + + file_path = doc.image + file_name = os.path.basename(file_path) + + if file_path.startswith('http'): + url = file_path + file_path = os.path.join('/tmp', file_name) + urllib.urlretrieve(url, file_path) + + with io.open(file_path, 'rb') as f: + doc.image = base64.b64encode(f.read()) + + doc.image_file_name = file_name + + return doc + diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json index 7423f2e8a63..3ace088a808 100644 --- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json +++ b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json @@ -1,55 +1,45 @@ { - "condition": "{\"publish_in_hub\": 1}", - "creation": "2017-09-07 13:27:52.726350", - "docstatus": 0, - "doctype": "Data Migration Mapping", + "condition": "{\"publish_in_hub\": 1}", + "creation": "2017-09-07 13:27:52.726350", + "docstatus": 0, + "doctype": "Data Migration Mapping", "fields": [ { - "is_child_table": 0, - "local_fieldname": "item_code", + "is_child_table": 0, + "local_fieldname": "item_code", "remote_fieldname": "item_code" - }, + }, { - "is_child_table": 0, - "local_fieldname": "item_name", + "is_child_table": 0, + "local_fieldname": "item_name", "remote_fieldname": "item_name" - }, + }, { - "is_child_table": 0, - "local_fieldname": "eval:frappe.db.get_default(\"company\")", - "remote_fieldname": "company_name" - }, + "is_child_table": 0, + "local_fieldname": "eval:frappe.db.get_value('Hub Settings' , 'Hub Settings', 'company_email')", + "remote_fieldname": "hub_seller" + }, { - "is_child_table": 0, - "local_fieldname": "image", + "is_child_table": 0, + "local_fieldname": "image", "remote_fieldname": "image" - }, + }, { - "is_child_table": 0, - "local_fieldname": "item_group", + "is_child_table": 0, + "local_fieldname": "item_group", "remote_fieldname": "item_group" - }, - { - "is_child_table": 0, - "local_fieldname": "eval:frappe.session.user", - "remote_fieldname": "seller" - }, - { - "is_child_table": 0, - "local_fieldname": "eval:frappe.db.get_default(\"country\")", - "remote_fieldname": "country" } - ], - "idx": 1, - "local_doctype": "Item", - "mapping_name": "Item to Hub Item", - "mapping_type": "Push", - "migration_id_field": "hub_sync_id", - "modified": "2018-02-14 15:57:05.595712", - "modified_by": "achilles@erpnext.com", - "name": "Item to Hub Item", - "owner": "Administrator", - "page_length": 10, - "remote_objectname": "Hub Item", + ], + "idx": 1, + "local_doctype": "Item", + "mapping_name": "Item to Hub Item", + "mapping_type": "Push", + "migration_id_field": "hub_sync_id", + "modified": "2018-08-01 16:37:09.170546", + "modified_by": "Administrator", + "name": "Item to Hub Item", + "owner": "Administrator", + "page_length": 10, + "remote_objectname": "Hub Item", "remote_primary_key": "item_code" } \ No newline at end of file diff --git a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json index d66ac24b1c2..1f772b68f0b 100644 --- a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json +++ b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json @@ -1,22 +1,19 @@ { - "creation": "2017-09-07 11:39:38.445902", - "docstatus": 0, - "doctype": "Data Migration Plan", - "idx": 1, + "creation": "2017-09-07 11:39:38.445902", + "docstatus": 0, + "doctype": "Data Migration Plan", + "idx": 1, "mappings": [ { - "enabled": 1, + "enabled": 1, "mapping": "Item to Hub Item" - }, - { - "enabled": 1, - "mapping": "Hub Message to Lead" } - ], - "modified": "2018-02-14 15:57:05.519715", - "modified_by": "achilles@erpnext.com", - "module": "Hub Node", - "name": "Hub Sync", - "owner": "Administrator", - "plan_name": "Hub Sync" + ], + "modified": "2018-08-01 16:37:09.027512", + "modified_by": "Administrator", + "module": "Hub Node", + "name": "Hub Sync", + "owner": "Administrator", + "plan_name": "Hub Sync", + "postprocess_method": "erpnext.hub_node.api.item_sync_postprocess" } \ No newline at end of file diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.js b/erpnext/hub_node/doctype/hub_settings/hub_settings.js index 29d870b371b..4bd33332766 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.js +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.js @@ -1,53 +1,23 @@ frappe.ui.form.on("Hub Settings", { refresh: function(frm) { + frm.disable_save(); frm.add_custom_button(__('Logs'), () => frappe.set_route('List', 'Data Migration Run', { data_migration_plan: 'Hub Sync' })); - frm.trigger("enabled"); - if (frm.doc.enabled) { frm.add_custom_button(__('Sync'), () => frm.call('sync')); } }, - onload: function(frm) { - let token = frappe.urllib.get_arg("access_token"); - if(token) { - let email = frm.get_field("user"); - console.log('token', frappe.urllib.get_arg("access_token")); - - get_user_details(frm, token, email); - let row = frappe.model.add_child(frm.doc, "Hub Users", "users"); - row.user = frappe.session.user; - } - - if(!frm.doc.country) { - frm.set_value("country", frappe.defaults.get_default("Country")); - } - if(!frm.doc.company) { - frm.set_value("company", frappe.defaults.get_default("Company")); - } - if(!frm.doc.user) { - frm.set_value("user", frappe.session.user); - } - }, + onload: function(frm) { }, onload_post_render: function(frm) { if(frm.get_field("unregister_from_hub").$input) frm.get_field("unregister_from_hub").$input.addClass("btn-danger"); }, on_update: function(frm) { }, - enabled: function(frm) { - if(!frm.doc.enabled) { - frm.trigger("set_enable_hub_primary_button"); - } else { - frm.page.set_primary_action(__("Save Settings"), () => { - frm.save(); - }); - } - }, hub_user_email: function(frm) { if(frm.doc.hub_user_email){ @@ -55,39 +25,6 @@ frappe.ui.form.on("Hub Settings", { } }, - set_enable_hub_primary_button: (frm) => { - frm.page.set_primary_action(__("Enable Hub"), () => { - if(frappe.session.user === "Administrator") { - frappe.msgprint(__("Please login as another user.")) - } else { - // frappe.verify_password(() => { - - // } ); - - frm.trigger("call_pre_reg"); - // frm.trigger("call_register"); - - } - }); - }, - - call_pre_reg: (frm) => { - this.frm.call({ - doc: this.frm.doc, - method: "pre_reg", - args: {}, - freeze: true, - callback: function(r) { - console.log(r.message); - authorize(frm, r.message.client_id, r.message.redirect_uri); - }, - onerror: function() { - frappe.msgprint(__("Wrong Password")); - frm.set_value("enabled", 0); - } - }); - }, - call_register: (frm) => { this.frm.call({ doc: this.frm.doc, @@ -111,67 +48,3 @@ frappe.ui.form.on("Hub Settings", { }); }, }); - -// let hub_url = 'https://hubmarket.org' -let hub_url = 'http://159.89.175.122' -// let hub_url = 'http://erpnext.hub:8000' - -function authorize(frm, client_id, redirect_uri) { - - // queryStringData is details of OAuth Client (Implicit Grant) on Custom App - var queryStringData = { - response_type : "token", - client_id : client_id, - redirect_uri : redirect_uri - } - - // Get current raw route and build url - const route = "/desk#" + frappe.get_raw_route_str(); - localStorage.removeItem("route"); // Clear previously set route if any - localStorage.setItem("route", route); - - // Go authorize! - let api_route = "/api/method/frappe.integrations.oauth2.authorize?"; - let url = hub_url + api_route + $.param(queryStringData); - window.location.replace(url, 'test'); -} - -function get_user_details(frm, token, email) { - console.log('user_details'); - var route = localStorage.getItem("route"); - if (token && route) { - // Clean up access token from route - frappe.set_route(frappe.get_route().join("/")) - - // query protected resource e.g. Hub Items with token - var call = { - "async": true, - "crossDomain": true, - "url": hub_url + "/api/resource/User", - "method": "GET", - "data": { - // "email": email, - "fields": '["name", "first_name", "language"]', - "limit_page_length": 1 - }, - "headers": { - "authorization": "Bearer " + token, - "content-type": "application/x-www-form-urlencoded" - } - } - $.ajax(call).done(function (response) { - // display openid profile - console.log('response', response); - - let data = response.data[0]; - frm.set_value("enabled", 1); - frm.set_value("hub_username", data.first_name); - frm.set_value("hub_user_status", "Starter"); - frm.set_value("language", data.language); - frm.save(); - - // clear route from localStorage - localStorage.removeItem("route"); - }); - } -} diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.json b/erpnext/hub_node/doctype/hub_settings/hub_settings.json index 7c7109c64c1..a0d8188d445 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.json +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.json @@ -14,20 +14,21 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "enabled", + "fieldname": "registered", "fieldtype": "Check", - "hidden": 1, + "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Enabled", + "label": "Registered", "length": 0, "no_copy": 0, "permlevel": 0, @@ -45,212 +46,21 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "suspended", + "fieldname": "sync_in_progress", "fieldtype": "Check", - "hidden": 1, + "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Suspended", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "hub_username", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hub Username", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "hub_user_status", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "language", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Language", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:(!doc.enabled)", - "columns": 0, - "depends_on": "", - "fieldname": "seller_profile_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company and Seller Profile", + "label": "Sync in Progress", "length": 0, "no_copy": 0, "permlevel": 0, @@ -268,6 +78,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -299,6 +110,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -331,6 +143,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -362,6 +175,39 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "site_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Site Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -394,11 +240,44 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company_logo", + "fieldname": "currency", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Currency", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "logo", "fieldtype": "Attach Image", "hidden": 0, "ignore_user_permissions": 0, @@ -425,11 +304,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "seller_description", + "fieldname": "company_description", "fieldtype": "Text Editor", "hidden": 0, "ignore_user_permissions": 0, @@ -456,6 +336,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -487,43 +368,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "users", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Users", - "length": 0, - "no_copy": 0, - "options": "Hub Users", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", + "depends_on": "", "fieldname": "publish_section", "fieldtype": "Section Break", "hidden": 0, @@ -551,6 +401,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -582,6 +433,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -614,6 +466,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -647,6 +500,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -679,11 +533,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "publish", + "depends_on": "", "fieldname": "last_sync_datetime", "fieldtype": "Datetime", "hidden": 0, @@ -700,7 +555,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -711,6 +566,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -744,6 +600,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -777,6 +634,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -817,8 +675,8 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-03-26 00:55:17.929140", - "modified_by": "test1@example.com", + "modified": "2018-07-30 10:43:28.818498", + "modified_by": "Administrator", "module": "Hub Node", "name": "Hub Settings", "name_case": "", @@ -826,7 +684,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 0, diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 15ee4b7da35..bfb332022a1 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -2,7 +2,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, requests, json +import frappe, requests, json, time from frappe.model.document import Document from frappe.utils import add_years, now, get_datetime, get_datetime_str @@ -10,33 +10,18 @@ from frappe import _ from erpnext.utilities.product import get_price, get_qty_in_stock from six import string_types -hub_url = "https://hubmarket.org" -# hub_url = "http://159.89.175.122" -# hub_url = "http://erpnext.hub:8000" - -class OAuth2Session(): - def __init__(self, headers): - self.headers = headers - def get(self, url, params, headers, verify): - res = requests.get(url, params=params, headers=self.headers, verify=verify) - return res - def post(self, url, data, verify): - res = requests.post(url, data=data, headers=self.headers, verify=verify) - return res - def put(self, url, data, verify): - res = requests.put(url, data=data, headers=self.headers, verify=verify) - return res - class HubSetupError(frappe.ValidationError): pass class HubSettings(Document): def validate(self): + protocol = 'http://' + self.site_name = protocol + frappe.local.site + ':' + str(frappe.conf.webserver_port) if self.publish_pricing and not self.selling_price_list: frappe.throw(_("Please select a Price List to publish pricing")) def get_hub_url(self): - return hub_url + return frappe.conf.hub_url def sync(self): """Create and execute Data Migration Run for Hub Sync plan""" @@ -45,68 +30,53 @@ class HubSettings(Document): doc = frappe.get_doc({ 'doctype': 'Data Migration Run', 'data_migration_plan': 'Hub Sync', - 'data_migration_connector': 'Hub Connector' + 'data_migration_connector': 'Hub Connector', + 'trigger_name': 'items-sync' }).insert() + self.sync_in_progress = 1 doc.run() - def pre_reg(self): - site_name = frappe.local.site + ':' + str(frappe.conf.webserver_port) - protocol = 'http://' - route = '/token' - data = { - 'site_name': site_name, - 'protocol': protocol, - 'route': route - } - - redirect_url = protocol + site_name + route - post_url = hub_url + '/api/method/hub.hub.api.pre_reg' - - response = requests.post(post_url, data=data) - response.raise_for_status() - message = response.json().get('message') - - if message and message.get('client_id'): - print("======CLIENT_ID======") - print(message.get('client_id')) - - return { - 'client_id': message.get('client_id'), - 'redirect_uri': redirect_url - } - - def register(self): """ Create a User on hub.erpnext.org and return username/password """ + + # TODO: site_name for cloud sites + protocol = 'http://' + self.site_name = protocol + frappe.local.site + ':' + str(frappe.conf.webserver_port) + data = { - 'email': frappe.session.user + 'profile': self.as_json() } - post_url = hub_url + '/api/method/hub.hub.api.register' + post_url = self.get_hub_url() + '/api/method/hub.hub.api.register' + + response = requests.post(post_url, data=data, headers = {'accept': 'application/json'}) - response = requests.post(post_url, data=data) response.raise_for_status() - message = response.json().get('message') - if message and message.get('password'): - self.user = frappe.session.user + if response.ok: + message = response.json().get('message') + else: + frappe.throw(json.loads(response.text)) + + if message.get('email'): self.create_hub_connector(message) - self.company = frappe.defaults.get_user_default('company') - self.enabled = 1 + self.registered = 1 self.save() - def unregister(self): - """ Disable the User on hub.erpnext.org""" + return message or None - hub_connector = frappe.get_doc( - 'Data Migration Connector', 'Hub Connector') + # def unregister(self): + # """ Disable the User on hub.erpnext.org""" - connection = hub_connector.get_connection() - response_doc = connection.update('User', frappe._dict({'enabled': 0}), hub_connector.username) + # hub_connector = frappe.get_doc( + # 'Data Migration Connector', 'Hub Connector') - if response_doc['enabled'] == 0: - self.enabled = 0 - self.save() + # connection = hub_connector.get_connection() + # response_doc = connection.update('User', frappe._dict({'enabled': 0}), hub_connector.username) + + # if response_doc['enabled'] == 0: + # self.enabled = 0 + # self.save() def create_hub_connector(self, message): if frappe.db.exists('Data Migration Connector', 'Hub Connector'): @@ -120,7 +90,7 @@ class HubSettings(Document): 'doctype': 'Data Migration Connector', 'connector_type': 'Frappe', 'connector_name': 'Hub Connector', - 'hostname': hub_url, + 'hostname': self.get_hub_url(), 'username': message['email'], 'password': message['password'] }).insert() @@ -143,3 +113,11 @@ def reset_hub_settings(last_sync_datetime = ""): def sync(): hub_settings = frappe.get_doc('Hub Settings') hub_settings.sync() + +@frappe.whitelist() +def register_seller(**kwargs): + settings = frappe.get_doc('Hub Settings') + settings.update(kwargs) + message = settings.register() + + return message.get('email') diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py new file mode 100644 index 00000000000..87d4e1b4adc --- /dev/null +++ b/erpnext/hub_node/legacy.py @@ -0,0 +1,178 @@ +from __future__ import unicode_literals +import frappe, requests, json +from frappe.utils import now, nowdate +from frappe.frappeclient import FrappeClient + +@frappe.whitelist() +def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None): + doctype = 'Hub Item' + hub_settings = frappe.get_doc('Hub Settings') + item_names_str = hub_settings.get('custom_data') or '[]' + item_names = json.loads(item_names_str) + filters = json.dumps({ + 'hub_item_code': ['in', item_names] + }) + return get_list(doctype, start, limit, fields, filters, order_by) + +@frappe.whitelist() +def update_wishlist_item(item_name, remove=0): + remove = int(remove) + hub_settings = frappe.get_doc('Hub Settings') + data = hub_settings.get('custom_data') + if not data or not json.loads(data): + data = '[]' + hub_settings.custom_data = data + hub_settings.save() + + item_names_str = data + item_names = json.loads(item_names_str) + if not remove and item_name not in item_names: + item_names.append(item_name) + if remove and item_name in item_names: + item_names.remove(item_name) + + item_names_str = json.dumps(item_names) + + hub_settings.custom_data = item_names_str + hub_settings.save() + +@frappe.whitelist() +def update_category(hub_item_code, category): + connection = get_hub_connection() + + # args = frappe._dict(dict( + # doctype='Hub Category', + # hub_category_name=category + # )) + # response = connection.insert('Hub Category', args) + + response = connection.update('Hub Item', frappe._dict(dict( + doctype='Hub Item', + hub_category = category + )), hub_item_code) + + return response + +def make_opportunity(buyer_name, email_id): + buyer_name = "HUB-" + buyer_name + + if not frappe.db.exists('Lead', {'email_id': email_id}): + lead = frappe.new_doc("Lead") + lead.lead_name = buyer_name + lead.email_id = email_id + lead.save(ignore_permissions=True) + + o = frappe.new_doc("Opportunity") + o.enquiry_from = "Lead" + o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"] + o.save(ignore_permissions=True) + +@frappe.whitelist() +def make_rfq_and_send_opportunity(item, supplier): + supplier = make_supplier(supplier) + contact = make_contact(supplier) + item = make_item(item) + rfq = make_rfq(item, supplier, contact) + status = send_opportunity(contact) + + return { + 'rfq': rfq, + 'hub_document_created': status + } + +def make_supplier(supplier): + # make supplier if not already exists + supplier = frappe._dict(json.loads(supplier)) + + if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}): + supplier_doc = frappe.get_doc({ + 'doctype': 'Supplier', + 'supplier_name': supplier.supplier_name, + 'supplier_group': supplier.supplier_group, + 'supplier_email': supplier.supplier_email + }).insert() + else: + supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name) + + return supplier_doc + +def make_contact(supplier): + contact_name = get_default_contact('Supplier', supplier.supplier_name) + # make contact if not already exists + if not contact_name: + contact = frappe.get_doc({ + 'doctype': 'Contact', + 'first_name': supplier.supplier_name, + 'email_id': supplier.supplier_email, + 'is_primary_contact': 1, + 'links': [ + {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} + ] + }).insert() + else: + contact = frappe.get_doc('Contact', contact_name) + + return contact + +def make_item(item): + # make item if not already exists + item = frappe._dict(json.loads(item)) + + if not frappe.db.exists('Item', {'item_code': item.item_code}): + item_doc = frappe.get_doc({ + 'doctype': 'Item', + 'item_code': item.item_code, + 'item_group': item.item_group, + 'is_item_from_hub': 1 + }).insert() + else: + item_doc = frappe.get_doc('Item', item.item_code) + + return item_doc + +def make_rfq(item, supplier, contact): + # make rfq + rfq = frappe.get_doc({ + 'doctype': 'Request for Quotation', + 'transaction_date': nowdate(), + 'status': 'Draft', + 'company': frappe.db.get_single_value('Hub Settings', 'company'), + 'message_for_supplier': 'Please supply the specified items at the best possible rates', + 'suppliers': [ + { 'supplier': supplier.name, 'contact': contact.name } + ], + 'items': [ + { + 'item_code': item.item_code, + 'qty': 1, + 'schedule_date': nowdate(), + 'warehouse': item.default_warehouse or get_root_of("Warehouse"), + 'description': item.description, + 'uom': item.stock_uom + } + ] + }).insert() + + rfq.save() + rfq.submit() + return rfq + +def send_opportunity(contact): + # Make Hub Message on Hub with lead data + doc = { + 'doctype': 'Lead', + 'lead_name': frappe.db.get_single_value('Hub Settings', 'company'), + 'email_id': frappe.db.get_single_value('Hub Settings', 'user') + } + + args = frappe._dict(dict( + doctype='Hub Message', + reference_doctype='Lead', + data=json.dumps(doc), + user=contact.email_id + )) + + connection = get_hub_connection() + response = connection.insert('Hub Message', args) + + return response.ok diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 20def27ced0..154f4407406 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -559,3 +559,5 @@ erpnext.patches.v11_0.update_allow_transfer_for_manufacture erpnext.patches.v11_0.rename_healthcare_doctype_and_fields erpnext.patches.v11_0.add_item_group_defaults erpnext.patches.v10_0.update_address_template_for_india +execute:frappe.delete_doc("Page", "hub") +erpnext.patches.v11_0.reset_publish_in_hub_for_all_items diff --git a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py new file mode 100644 index 00000000000..fac772ccdd9 --- /dev/null +++ b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py @@ -0,0 +1,5 @@ +import frappe + +def execute(): + frappe.reload_doc('stock', 'doctype', 'item') + frappe.db.sql("""update `tabItem` set publish_in_hub = 0""") diff --git a/erpnext/public/build.json b/erpnext/public/build.json index ed4ebababee..7bcf99bd2f3 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -7,6 +7,9 @@ "public/js/website_utils.js", "public/js/shopping_cart.js" ], + "js/marketplace.min.js": [ + "public/js/hub/marketplace.js" + ], "js/erpnext.min.js": [ "public/js/conf.js", "public/js/utils.js", diff --git a/erpnext/public/js/hub/helpers.js b/erpnext/public/js/hub/helpers.js new file mode 100644 index 00000000000..22e35c3d89e --- /dev/null +++ b/erpnext/public/js/hub/helpers.js @@ -0,0 +1,143 @@ +function get_empty_state(message, action) { + return `
+

${message}

+ ${action ? `

${action}

`: ''} +
`; +} + +function get_item_card_container_html(items, title='', get_item_html = get_item_card_html) { + const items_html = (items || []).map(item => get_item_html(item)).join(''); + const title_html = title + ? `
+ ${title} +
` + : ''; + + const html = `
+ ${title_html} + ${items_html} +
`; + + return html; +} + +function get_item_card_html(item) { + const item_name = item.item_name || item.name; + const title = strip_html(item_name); + const img_url = item.image; + const company_name = item.company; + + // Subtitle + let subtitle = [comment_when(item.creation)]; + const rating = item.average_rating; + if (rating > 0) { + subtitle.push(rating + ``) + } + subtitle.push(company_name); + + let dot_spacer = ''; + subtitle = subtitle.join(dot_spacer); + + const item_html = ` +
+
+
+
${title}
+
${subtitle}
+
+
+ +
+
+
+
+ `; + + return item_html; +} + +function get_local_item_card_html(item) { + const item_name = item.item_name || item.name; + const title = strip_html(item_name); + const img_url = item.image; + const company_name = item.company; + + const is_active = item.publish_in_hub; + const id = item.hub_item_code || item.item_code; + + // Subtitle + let subtitle = [comment_when(item.creation)]; + const rating = item.average_rating; + if (rating > 0) { + subtitle.push(rating + ``) + } + subtitle.push(company_name); + + let dot_spacer = ''; + subtitle = subtitle.join(dot_spacer); + + const edit_item_button = `
+ +
`; + + const item_html = ` +
+
+
+
${title}
+
${subtitle}
+ +
+
+ +
+
+ ${edit_item_button} +
+
+
+
+
+ `; + + return item_html; +} + + +function get_rating_html(rating) { + let rating_html = ``; + for (var i = 0; i < 5; i++) { + let star_class = 'fa-star'; + if (i >= rating) star_class = 'fa-star-o'; + rating_html += ``; + } + return rating_html; +} + +function make_search_bar({wrapper, on_search, placeholder = __('Search for anything')}) { + const $search = $(` +
+ +
` + ); + wrapper.append($search); + const $search_input = $search.find('input'); + + $search_input.on('keydown', frappe.utils.debounce((e) => { + if (e.which === frappe.ui.keyCode.ENTER) { + const search_value = $search_input.val(); + on_search(search_value); + } + }, 300)); +} + +export { + get_empty_state, + get_item_card_container_html, + get_item_card_html, + get_local_item_card_html, + get_rating_html, + make_search_bar, +} \ No newline at end of file diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js new file mode 100644 index 00000000000..6786cf6f90a --- /dev/null +++ b/erpnext/public/js/hub/hub_call.js @@ -0,0 +1,44 @@ +frappe.provide('hub'); +frappe.provide('erpnext.hub'); + +erpnext.hub.cache = {}; +hub.call = function call_hub_method(method, args={}) { + return new Promise((resolve, reject) => { + + // cache + const key = method + JSON.stringify(args); + if (erpnext.hub.cache[key]) { + resolve(erpnext.hub.cache[key]); + } + + // cache invalidation after 5 minutes + const timeout = 5 * 60 * 1000; + + setTimeout(() => { + delete erpnext.hub.cache[key]; + }, timeout); + + frappe.call({ + method: 'erpnext.hub_node.api.call_hub_method', + args: { + method, + params: args + } + }) + .then(r => { + if (r.message) { + if (r.message.error) { + frappe.throw({ + title: __('Marketplace Error'), + message: r.message.error + }); + } + + erpnext.hub.cache[key] = r.message; + resolve(r.message) + } + reject(r) + }) + .fail(reject) + }); +} diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js index d656605c07b..f933600375c 100644 --- a/erpnext/public/js/hub/hub_factory.js +++ b/erpnext/public/js/hub/hub_factory.js @@ -1,80 +1,32 @@ -frappe.provide('erpnext.hub.pages'); +frappe.provide('erpnext.hub'); -frappe.views.HubFactory = class HubFactory extends frappe.views.Factory { - make(route) { - const page_name = frappe.get_route_str(); - const page = route[1]; +frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views.Factory { + show() { + if (frappe.pages.marketplace) { + frappe.container.change_to('marketplace'); + erpnext.hub.marketplace.refresh(); + } else { + this.make('marketplace'); + } + } - const assets = { - 'List': [ - '/assets/erpnext/js/hub/hub_listing.js', - ], - 'Form': [ - '/assets/erpnext/js/hub/hub_form.js' - ] - }; - frappe.model.with_doc('Hub Settings', 'Hub Settings', () => { - this.hub_settings = frappe.get_doc('Hub Settings'); + make(page_name) { + const assets = [ + '/assets/js/marketplace.min.js' + ]; - if (!erpnext.hub.pages[page_name]) { - if(!frappe.is_online()) { - this.render_offline_card(); - return; - } - if (!route[2]) { - frappe.require(assets['List'], () => { - if(page === 'Favourites') { - erpnext.hub.pages[page_name] = new erpnext.hub['Favourites']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - } else { - erpnext.hub.pages[page_name] = new erpnext.hub[page+'Listing']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - } - }); - } else if (!route[3]){ - frappe.require(assets['Form'], () => { - erpnext.hub.pages[page_name] = new erpnext.hub[page+'Page']({ - unique_id: route[2], - doctype: route[2], - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - }); - } else { - frappe.require(assets['List'], () => { - frappe.route_options = {}; - frappe.route_options["company_name"] = route[2] - erpnext.hub.pages[page_name] = new erpnext.hub['ItemListing']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - }); - } - window.hub_page = erpnext.hub.pages[page_name]; - } else { - frappe.container.change_to(page_name); - window.hub_page = erpnext.hub.pages[page_name]; - } + frappe.require(assets, () => { + erpnext.hub.marketplace = new erpnext.hub.Marketplace({ + parent: this.make_page(true, page_name) + }); }); } +} - render_offline_card() { - let html = `
-
- ${'Failed to connect'} -
-

${ __("Please check your network connection.") }

-
- ${ __("Reload") }
-
`; - - let page = $('#body_div'); - page.append(html); - - return; - } -}; +$(document).on('toolbar_setup', () => { + $('#toolbar-user .navbar-reload').after(` +
  • + ${__('Marketplace')} +
  • + `) +}) diff --git a/erpnext/public/js/hub/hub_form.js b/erpnext/public/js/hub/hub_form.js deleted file mode 100644 index 9287e6d54f4..00000000000 --- a/erpnext/public/js/hub/hub_form.js +++ /dev/null @@ -1,493 +0,0 @@ -frappe.provide('erpnext.hub'); - -erpnext.hub.HubDetailsPage = class HubDetailsPage extends frappe.views.BaseList { - setup_defaults() { - super.setup_defaults(); - this.method = 'erpnext.hub_node.get_details'; - const route = frappe.get_route(); - // this.page_name = route[2]; - } - - setup_fields() { - return this.get_meta() - .then(r => { - this.meta = r.message.meta || this.meta; - this.categories = r.message.categories || []; - this.bootstrap_data(r.message); - - this.getFormFields(); - }); - } - - bootstrap_data() { } - - get_meta() { - return new Promise(resolve => - frappe.call('erpnext.hub_node.get_meta', {doctype: 'Hub ' + this.doctype}, resolve)); - } - - - set_breadcrumbs() { - frappe.breadcrumbs.add({ - label: __('Hub'), - route: '#Hub/' + this.doctype, - type: 'Custom' - }); - } - - setup_side_bar() { - this.sidebar = new frappe.ui.Sidebar({ - wrapper: this.$page.find('.layout-side-section'), - css_class: 'hub-form-sidebar' - }); - } - - setup_filter_area() { } - - setup_sort_selector() { } - - // let category = this.quick_view.get_values().hub_category; - // return new Promise((resolve, reject) => { - // frappe.call({ - // method: 'erpnext.hub_node.update_category', - // args: { - // hub_item_code: values.hub_item_code, - // category: category, - // }, - // callback: (r) => { - // resolve(); - // }, - // freeze: true - // }).fail(reject); - // }); - - get_timeline() { - return `
    -
    -
    -
    - -
    -
    -
    `; - } - - get_footer() { - return `
    `; - } - - get_args() { - return { - hub_sync_id: this.unique_id, - doctype: 'Hub ' + this.doctype - }; - } - - prepare_data(r) { - this.data = r.message; - } - - update_data(r) { - this.data = r.message; - } - - render() { - const image_html = this.data[this.image_field_name] ? - ` - ` : - `
    ${frappe.get_abbr(this.page_title)}
    `; - - this.sidebar.remove_item('image'); - this.sidebar.add_item({ - name: 'image', - label: image_html - }); - - if(!this.form) { - let fields = this.formFields; - this.form = new frappe.ui.FieldGroup({ - parent: this.$result, - fields - }); - this.form.make(); - } - - if(this.data.hub_category) { - this.form.fields_dict.set_category.hide(); - } - - this.form.set_values(this.data); - this.$result.show(); - - this.$timelineList && this.$timelineList.empty(); - if(this.data.reviews && this.data.reviews.length) { - this.data.reviews.map(review => { - this.addReviewToTimeline(review); - }) - } - - this.postRender() - } - - postRender() {} - - attachFooter() { - let footerHtml = ``; - - let parent = $('
    ').appendTo(this.page.main.parent()); - this.$footer = $(footerHtml).appendTo(parent); - } - - attachTimeline() { - let timelineHtml = `
    -
    -
    -
    - -
    -
    -
    `; - - let parent = this.$footer.find(".form-comments"); - this.$timeline = $(timelineHtml).appendTo(parent); - - this.$timelineList = this.$timeline.find(".timeline-items"); - } - - attachReviewArea() { - this.comment_area = new frappe.ui.ReviewArea({ - parent: this.$footer.find('.timeline-head'), - mentions: [], - on_submit: (val) => { - val.user = frappe.session.user; - val.username = frappe.session.user_fullname; - frappe.call({ - method: 'erpnext.hub_node.send_review', - args: { - hub_item_code: this.data.hub_item_code, - review: val - }, - callback: (r) => { - this.refresh(); - this.comment_area.reset(); - }, - freeze: true - }); - } - }); - } - - addReviewToTimeline(data) { - let username = data.username || data.user || __("Anonymous") - let imageHtml = data.user_image - ? `
    ` - : `
    ${frappe.get_abbr(username)}
    ` - - let editHtml = data.own - ? ` -
    - - ${'data.edit'} - -
    ` - : ''; - - let ratingHtml = ''; - - for(var i = 0; i < 5; i++) { - let starIcon = 'fa-star-o' - if(i < data.rating) { - starIcon = 'fa-star'; - } - ratingHtml += ``; - } - - $(this.getTimelineItem(data, imageHtml, editHtml, ratingHtml)) - .appendTo(this.$timelineList); - } - - getTimelineItem(data, imageHtml, editHtml, ratingHtml) { - return `
    - - -
    -
    -
    ${editHtml}
    - -
    - - ${imageHtml} - - -
    - - - ${data.username} - - - - - - - - - ${''} - -
    -
    -
    -
    -

    - ${data.subject} -

    - -
    - -

    - ${ratingHtml} -

    - -
    -

    - ${data.content} -

    -
    -
    -
    -
    -
    `; - } - - prepareFormFields(fields, fieldnames) { - return fields - .filter(field => fieldnames.includes(field.fieldname)) - .map(field => { - let { - label, - fieldname, - fieldtype, - } = field; - let read_only = 1; - return { - label, - fieldname, - fieldtype, - read_only, - }; - }); - } -}; - -erpnext.hub.ItemPage = class ItemPage extends erpnext.hub.HubDetailsPage { - constructor(opts) { - super(opts); - - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Item'; - this.image_field_name = 'image'; - } - - setup_page_head() { - super.setup_page_head(); - this.set_primary_action(); - } - - setup_side_bar() { - super.setup_side_bar(); - this.attachFooter(); - this.attachTimeline(); - this.attachReviewArea(); - } - - set_primary_action() { - let item = this.data; - this.page.set_primary_action(__('Request a Quote'), () => { - this.show_rfq_modal() - .then(values => { - item.item_code = values.item_code; - delete values.item_code; - - const supplier = values; - return [item, supplier]; - }) - .then(([item, supplier]) => { - return this.make_rfq(item, supplier, this.page.btn_primary); - }) - .then(r => { - console.log(r); - if (r.message && r.message.rfq) { - this.page.btn_primary.addClass('disabled').html(` ${__('Quote Requested')}`); - } else { - throw r; - } - }) - .catch((e) => { - console.log(e); //eslint-disable-line - }); - }, 'octicon octicon-plus'); - } - - prepare_data(r) { - super.prepare_data(r); - this.page.set_title(this.data["item_name"]); - } - - make_rfq(item, supplier, btn) { - console.log(supplier); - return new Promise((resolve, reject) => { - frappe.call({ - method: 'erpnext.hub_node.make_rfq_and_send_opportunity', - args: { item, supplier }, - callback: resolve, - btn, - }).fail(reject); - }); - } - - postRender() { - this.categoryDialog = new frappe.ui.Dialog({ - title: __('Suggest Category'), - fields: [ - { - label: __('Category'), - fieldname: 'category', - fieldtype: 'Autocomplete', - options: this.categories, - reqd: 1 - } - ], - primary_action_label: __("Send"), - primary_action: () => { - let values = this.categoryDialog.get_values(); - frappe.call({ - method: 'erpnext.hub_node.update_category', - args: { - hub_item_code: this.data.hub_item_code, - category: values.category - }, - callback: () => { - this.categoryDialog.hide(); - this.refresh(); - }, - freeze: true - }).fail(() => {}); - } - }); - } - - getFormFields() { - let colOneFieldnames = ['item_name', 'item_code', 'description']; - let colTwoFieldnames = ['seller', 'company_name', 'country']; - let colOneFields = this.prepareFormFields(this.meta.fields, colOneFieldnames); - let colTwoFields = this.prepareFormFields(this.meta.fields, colTwoFieldnames); - - let miscFields = [ - { - label: __('Category'), - fieldname: 'hub_category', - fieldtype: 'Data', - read_only: 1 - }, - - { - label: __('Suggest Category?'), - fieldname: 'set_category', - fieldtype: 'Button', - click: () => { - this.categoryDialog.show(); - } - }, - - { - fieldname: 'cb1', - fieldtype: 'Column Break' - } - ]; - this.formFields = colOneFields.concat(miscFields, colTwoFields); - } - - show_rfq_modal() { - let item = this.data; - return new Promise(res => { - let fields = [ - { label: __('Item Code'), fieldtype: 'Data', fieldname: 'item_code', default: item.item_code }, - { fieldtype: 'Column Break' }, - { label: __('Item Group'), fieldtype: 'Link', fieldname: 'item_group', default: item.item_group }, - { label: __('Supplier Details'), fieldtype: 'Section Break' }, - { label: __('Supplier Name'), fieldtype: 'Data', fieldname: 'supplier_name', default: item.company_name }, - { label: __('Supplier Email'), fieldtype: 'Data', fieldname: 'supplier_email', default: item.seller }, - { fieldtype: 'Column Break' }, - { label: __('Supplier Group'), fieldname: 'supplier_group', - fieldtype: 'Link', options: 'Supplier Group' } - ]; - fields = fields.map(f => { f.reqd = 1; return f; }); - - const d = new frappe.ui.Dialog({ - title: __('Request for Quotation'), - fields: fields, - primary_action_label: __('Send'), - primary_action: (values) => { - res(values); - d.hide(); - } - }); - - d.show(); - }); - } -} - -erpnext.hub.CompanyPage = class CompanyPage extends erpnext.hub.HubDetailsPage { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Company'; - this.image_field_name = 'company_logo'; - } - - prepare_data(r) { - super.prepare_data(r); - this.page.set_title(this.data["company_name"]); - } - - getFormFields() { - let fieldnames = ['company_name', 'description', 'route', 'country', 'seller', 'site_name'];; - this.formFields = this.prepareFormFields(this.meta.fields, fieldnames); - } -} diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js deleted file mode 100644 index 0ff79708d77..00000000000 --- a/erpnext/public/js/hub/hub_listing.js +++ /dev/null @@ -1,718 +0,0 @@ -frappe.provide('erpnext.hub'); - -erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { - setup_defaults() { - super.setup_defaults(); - this.page_title = __(''); - this.method = 'erpnext.hub_node.get_list'; - - this.cache = {}; - - const route = frappe.get_route(); - this.page_name = route[1]; - - this.menu_items = this.menu_items.concat(this.get_menu_items()); - - this.imageFieldName = 'image'; - - this.show_filters = 0; - } - - set_title() { - const title = this.page_title; - let iconHtml = ``; - let titleHtml = `${title}`; - this.page.set_title(iconHtml + titleHtml, '', false, title); - } - - setup_fields() { - return this.get_meta() - .then(r => { - this.meta = r.message.meta || this.meta; - frappe.model.sync(this.meta); - this.bootstrap_data(r.message); - - this.prepareFormFields(); - }); - } - - get_meta() { - return new Promise(resolve => - frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve)); - } - - set_breadcrumbs() { } - - prepareFormFields() { } - - bootstrap_data() { } - - get_menu_items() { - const items = [ - { - label: __('Hub Settings'), - action: () => frappe.set_route('Form', 'Hub Settings'), - standard: true - }, - { - label: __('Favourites'), - action: () => frappe.set_route('Hub', 'Favourites'), - standard: true - } - ]; - - return items; - } - - setup_side_bar() { - this.sidebar = new frappe.ui.Sidebar({ - wrapper: this.page.wrapper.find('.layout-side-section'), - css_class: 'hub-sidebar' - }); - } - - setup_sort_selector() { - this.sort_selector = new frappe.ui.SortSelector({ - parent: this.filter_area.$filter_list_wrapper, - doctype: this.doctype, - args: this.order_by, - onchange: () => this.refresh(true) - }); - } - - setup_view() { - if(frappe.route_options){ - const filters = []; - for (let field in frappe.route_options) { - var value = frappe.route_options[field]; - this.page.fields_dict[field].set_value(value); - } - } - } - - get_args() { - return { - doctype: this.doctype, - start: this.start, - limit: this.page_length, - order_by: this.order_by, - // fields: this.fields, - filters: this.get_filters_for_args() - }; - } - - update_data(r) { - const data = r.message; - - if (this.start === 0) { - this.data = data; - } else { - this.data = this.data.concat(data); - } - - this.data_dict = {}; - } - - freeze(toggle) { } - - render() { - this.data_dict = {}; - this.render_image_view(); - - this.setup_quick_view(); - this.setup_like(); - } - - render_offline_card() { - let html = `
    -
    - - {{ _("Payment Cancelled") }} -
    -

    ${ __("Your payment is cancelled.") }

    -
    - ${ __("Continue") }
    -
    `; - - let page = this.page.wrapper.find('.layout-side-section') - page.append(html); - - return; - } - - render_image_view() { - var html = this.data.map(this.item_html.bind(this)).join(""); - - if (this.start === 0) { - // ${this.getHeaderHtml()} - this.$result.html(` -
    - ${html} -
    - `); - } - - if(this.data.length) { - this.doc = this.data[0]; - } - - this.data.map(this.loadImage.bind(this)); - - this.data_dict = {}; - this.data.map(d => { - this.data_dict[d.hub_item_code] = d; - }); - } - - getHeaderHtml(title, image, content) { - // let company_html = - return ` -
    -
    -
    - ${title} -
    -
    -
    - ${title} -
    - - ${content} - -
    -
    -
    - `; - } - - renderHeader() { - return `
    -
    -
    - Riadco Group - Products by Blah blah -
    -
    -
    -
    - - -
    -
    -
    - ${''} -
    -
    `; - } - - get_image_html(encoded_name, src, alt_text) { - return `${ alt_text }`; - } - - get_image_placeholder(title) { - return `${ frappe.get_abbr(title) }`; - } - - loadImage(item) { - item._name = encodeURI(item.name); - const encoded_name = item._name; - const title = strip_html(item[this.meta.title_field || 'name']); - - let placeholder = this.get_image_placeholder(title); - let $container = this.$result.find(`.image-field[data-name="${encoded_name}"]`); - - if(!item[this.imageFieldName]) { - $container.prepend(placeholder); - $container.addClass('no-image'); - } - - frappe.load_image(item[this.imageFieldName], - (imageObj) => { - $container.prepend(imageObj) - }, - () => { - $container.prepend(placeholder); - $container.addClass('no-image'); - }, - (imageObj) => { - imageObj.title = encoded_name; - imageObj.alt = title; - } - ) - } - - setup_quick_view() { - if(this.quick_view) return; - - this.quick_view = new frappe.ui.Dialog({ - title: 'Quick View', - fields: this.formFields - }); - this.quick_view.set_primary_action(__('Request a Quote'), () => { - this.show_rfq_modal() - .then(values => { - item.item_code = values.item_code; - delete values.item_code; - - const supplier = values; - return [item, supplier]; - }) - .then(([item, supplier]) => { - return this.make_rfq(item, supplier, this.page.btn_primary); - }) - .then(r => { - console.log(r); - if (r.message && r.message.rfq) { - this.page.btn_primary.addClass('disabled').html(` ${__('Quote Requested')}`); - } else { - throw r; - } - }) - .catch((e) => { - console.log(e); //eslint-disable-line - }); - }, 'octicon octicon-plus'); - - this.$result.on('click', '.btn.zoom-view', (e) => { - e.preventDefault(); - e.stopPropagation(); - var name = $(e.target).attr('data-name'); - name = decodeURIComponent(name); - - this.quick_view.set_title(name); - let values = this.data_dict[name]; - this.quick_view.set_values(values); - - let fields = []; - - this.quick_view.show(); - - return false; - }); - } - - setup_like() { - if(this.setup_like_done) return; - this.setup_like_done = 1; - this.$result.on('click', '.btn.like-button', (e) => { - if($(e.target).hasClass('changing')) return; - $(e.target).addClass('changing'); - - e.preventDefault(); - e.stopPropagation(); - - var name = $(e.target).attr('data-name'); - name = decodeURIComponent(name); - let values = this.data_dict[name]; - - let heart = $(e.target); - if(heart.hasClass('like-button')) { - heart = $(e.target).find('.octicon'); - } - - let remove = 1; - - if(heart.hasClass('liked')) { - // unlike - heart.removeClass('liked'); - } else { - // like - remove = 0; - heart.addClass('liked'); - } - - frappe.call({ - method: 'erpnext.hub_node.update_wishlist_item', - args: { - item_name: values.hub_item_code, - remove: remove - }, - callback: (r) => { - let message = __("Added to Favourites"); - if(remove) { - message = __("Removed from Favourites"); - } - frappe.show_alert(message); - }, - freeze: true - }); - - $(e.target).removeClass('changing'); - return false; - }); - } -} - -erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Hub Item'; - this.page_title = __('Marketplace'); - this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country']; - this.filters = []; - } - - render() { - this.data_dict = {}; - this.render_image_view(); - - this.setup_quick_view(); - this.setup_like(); - } - - bootstrap_data(response) { - let companies = response.companies.map(d => d.name); - this.custom_filter_configs = [ - { - fieldtype: 'Autocomplete', - label: __('Select Company'), - condition: 'like', - fieldname: 'company_name', - options: companies - }, - { - fieldtype: 'Link', - label: __('Select Country'), - options: 'Country', - condition: 'like', - fieldname: 'country' - } - ]; - } - - prepareFormFields() { - let fieldnames = ['item_name', 'description', 'company_name', 'country']; - this.formFields = this.meta.fields - .filter(field => fieldnames.includes(field.fieldname)) - .map(field => { - let { - label, - fieldname, - fieldtype, - } = field; - let read_only = 1; - return { - label, - fieldname, - fieldtype, - read_only, - }; - }); - - this.formFields.unshift({ - label: 'image', - fieldname: 'image', - fieldtype: 'Attach Image' - }); - } - - setup_side_bar() { - super.setup_side_bar(); - - let $pitch = $(`
    -
    Sell on HubMarket
    -

    Over 2000 products listed. Register your company to start selling.

    -
    `); - - this.sidebar.$sidebar.append($pitch); - - this.category_tree = new frappe.ui.Tree({ - parent: this.sidebar.$sidebar, - label: 'All Categories', - expandable: true, - - args: {parent: this.current_category}, - method: 'erpnext.hub_node.get_categories', - on_click: (node) => { - this.update_category(node.label); - } - }); - - this.sidebar.add_item({ - label: __('Companies'), - on_click: () => frappe.set_route('Hub', 'Company') - }, undefined, true); - - this.sidebar.add_item({ - label: this.hub_settings.company, - on_click: () => frappe.set_route('Form', 'Company', this.hub_settings.company) - }, __("Account")); - - this.sidebar.add_item({ - label: __("Favourites"), - on_click: () => frappe.set_route('Hub', 'Favourites') - }, __("Account")); - - this.sidebar.add_item({ - label: __("Settings"), - on_click: () => frappe.set_route('Form', 'Hub Settings') - }, __("Account")); - } - - update_category(label) { - this.current_category = (label=='All Categories') ? undefined : label; - this.refresh(); - } - - get_filters_for_args() { - if(!this.filter_area) return; - let filters = {}; - this.filter_area.get().forEach(f => { - let field = f[1] !== 'name' ? f[1] : 'item_name'; - filters[field] = [f[2], f[3]]; - }); - if(this.current_category) { - filters['hub_category'] = this.current_category; - } - return filters; - } - - update_data(r) { - super.update_data(r); - - this.data_dict = {}; - this.data.map(d => { - this.data_dict[d.hub_item_code] = d; - }); - } - - item_html(item) { - item._name = encodeURI(item.name); - const encoded_name = item._name; - const title = strip_html(item[this.meta.title_field || 'name']); - const _class = !item[this.imageFieldName] ? 'no-image' : ''; - const route = `#Hub/Item/${item.hub_item_code}`; - const company_name = item['company_name']; - - const reviewLength = (item.reviews || []).length; - const ratingAverage = reviewLength - ? item.reviews - .map(r => r.rating) - .reduce((a, b) => a + b, 0)/reviewLength - : -1; - - let ratingHtml = ``; - - for(var i = 0; i < 5; i++) { - let starClass = 'fa-star'; - if(i >= ratingAverage) starClass = 'fa-star-o'; - ratingHtml += ``; - } - - let item_html = ` -
    -
    -
    - - ${title} - -
    -
    - ${ratingHtml} - (${reviewLength}) -
    - -
    - - -
    - `; - - return item_html; - } - -}; - -erpnext.hub.Favourites = class Favourites extends erpnext.hub.ItemListing { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Hub Item'; - this.page_title = __('Favourites'); - this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country']; - this.filters = []; - this.method = 'erpnext.hub_node.get_item_favourites'; - } - - setup_filter_area() { } - - setup_sort_selector() { } - - // setupHe - - getHeaderHtml() { - return ''; - } - - get_args() { - return { - start: this.start, - limit: this.page_length, - order_by: this.order_by, - fields: this.fields - }; - } - - bootstrap_data(response) { } - - prepareFormFields() { } - - setup_side_bar() { - this.sidebar = new frappe.ui.Sidebar({ - wrapper: this.page.wrapper.find('.layout-side-section'), - css_class: 'hub-sidebar' - }); - - this.sidebar.add_item({ - label: __('Back to Products'), - on_click: () => frappe.set_route('Hub', 'Item') - }); - } - - update_category(label) { - this.current_category = (label=='All Categories') ? undefined : label; - this.refresh(); - } - - get_filters_for_args() { - if(!this.filter_area) return; - let filters = {}; - this.filter_area.get().forEach(f => { - let field = f[1] !== 'name' ? f[1] : 'item_name'; - filters[field] = [f[2], f[3]]; - }); - if(this.current_category) { - filters['hub_category'] = this.current_category; - } - return filters; - } - - update_data(r) { - super.update_data(r); - - this.data_dict = {}; - this.data.map(d => { - this.data_dict[d.hub_item_code] = d; - }); - } -}; - -erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing { - constructor(opts) { - super(opts); - this.show(); - } - - render() { - this.data_dict = {}; - this.render_image_view(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Hub Company'; - this.page_title = __('Companies'); - this.fields = ['company_logo', 'name', 'site_name', 'seller_city', 'seller_description', 'seller', 'country', 'company_name']; - this.filters = []; - this.custom_filter_configs = [ - { - fieldtype: 'Link', - label: 'Country', - options: 'Country', - condition: 'like', - fieldname: 'country' - } - ]; - this.imageFieldName = 'company_logo'; - } - - setup_side_bar() { - this.sidebar = new frappe.ui.Sidebar({ - wrapper: this.page.wrapper.find('.layout-side-section'), - css_class: 'hub-sidebar' - }); - - this.sidebar.add_item({ - label: __('Back to Products'), - on_click: () => frappe.set_route('Hub', 'Item') - }); - } - - get_filters_for_args() { - let filters = {}; - this.filter_area.get().forEach(f => { - let field = f[1] !== 'name' ? f[1] : 'company_name'; - filters[field] = [f[2], f[3]]; - }); - return filters; - } - - item_html(company) { - company._name = encodeURI(company.company_name); - const encoded_name = company._name; - const title = strip_html(company.company_name); - const _class = !company[this.imageFieldName] ? 'no-image' : ''; - const company_name = company['company_name']; - const route = `#Hub/Company/${company_name}`; - - let image_html = company.company_logo ? - `` : - `
    ${frappe.get_abbr(company.company_name)}
    `; - - let item_html = ` -
    -
    -
    - - ${title} - -
    -
    - - -
    - `; - - return item_html; - } - -}; \ No newline at end of file diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js new file mode 100644 index 00000000000..18a1bb06294 --- /dev/null +++ b/erpnext/public/js/hub/marketplace.js @@ -0,0 +1,219 @@ +// pages +import './pages/home'; +import './pages/favourites'; +import './pages/search'; +import './pages/category'; +import './pages/item'; +import './pages/seller'; +import './pages/register'; +import './pages/profile'; +import './pages/publish'; +import './pages/published_products'; +import './pages/messages'; +import './pages/not_found'; + +// helpers +import './helpers'; +import './hub_call'; + +frappe.provide('hub'); +frappe.provide('erpnext.hub'); + +erpnext.hub.Marketplace = class Marketplace { + constructor({ parent }) { + this.$parent = $(parent); + this.page = parent.page; + + frappe.db.get_doc('Hub Settings') + .then(doc => { + hub.settings = doc; + this.registered = doc.registered; + + this.setup_header(); + this.make_sidebar(); + this.make_body(); + this.setup_events(); + this.refresh(); + }); + } + + setup_header() { + this.page.set_title(__('Marketplace')); + } + + setup_events() { + this.$parent.on('click', '[data-route]', (e) => { + const $target = $(e.currentTarget); + const route = $target.data().route; + frappe.set_route(route); + }); + } + + make_sidebar() { + this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); + + this.make_sidebar_nav_buttons(); + this.make_sidebar_categories(); + } + + make_sidebar_nav_buttons() { + let $nav_group = this.$sidebar.find('[data-nav-buttons]'); + if (!$nav_group.length) { + $nav_group = $('