diff --git a/erpnext/config/website.py b/erpnext/config/website.py index 5f77d9fb26..237c49c9af 100644 --- a/erpnext/config/website.py +++ b/erpnext/config/website.py @@ -5,6 +5,11 @@ def get_data(): { "label": _("Portal"), "items": [ + { + "type": "doctype", + "name": "Homepage", + "description": _("Settings for website homepage"), + }, { "type": "doctype", "name": "Shopping Cart Settings", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index d37c4beb4a..85fee1f39a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -139,7 +139,11 @@ doc_events = { 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice', 'Project', 'Issue'): { 'on_change': 'erpnext.accounts.party_status.notify_status' - } + }, + + "Website Settings": { + "validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products" + } } scheduler_events = { diff --git a/erpnext/modules.txt b/erpnext/modules.txt index dfca2f27c5..8a495478f9 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -11,3 +11,4 @@ Support Utilities Shopping Cart Hub Node +Portal \ No newline at end of file diff --git a/erpnext/portal/__init__.py b/erpnext/portal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/__init__.py b/erpnext/portal/doctype/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/homepage/__init__.py b/erpnext/portal/doctype/homepage/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/homepage/homepage.js b/erpnext/portal/doctype/homepage/homepage.js new file mode 100644 index 0000000000..df7f5ce6ae --- /dev/null +++ b/erpnext/portal/doctype/homepage/homepage.js @@ -0,0 +1,39 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Homepage', { + refresh: function(frm) { + + }, +}); + +frappe.ui.form.on('Homepage Featured Product', { + item_code: function(frm, cdt, cdn) { + var featured_product = frappe.model.get_doc(cdt, cdn); + if (featured_product.item_code) { + frappe.call({ + method: 'frappe.client.get_value', + args: { + 'doctype': 'Item', + 'filters': featured_product.item_code, + 'fieldname': [ + 'item_name', + 'web_long_description', + 'description', + 'image', + 'thumbnail' + ] + }, + callback: function(r) { + if (!r.exc) { + $.extend(featured_product, r.message); + if (r.message.web_long_description) { + featured_product.description = r.message.web_long_description; + } + frm.refresh_field('products'); + } + } + }); + } + } +}); diff --git a/erpnext/portal/doctype/homepage/homepage.json b/erpnext/portal/doctype/homepage/homepage.json new file mode 100644 index 0000000000..7078ef9a58 --- /dev/null +++ b/erpnext/portal/doctype/homepage/homepage.json @@ -0,0 +1,208 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "", + "creation": "2016-04-22 05:27:52.109319", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Company Tagline for website homepage", + "fieldname": "tag_line", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Tag Line", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Company Description for website homepage", + "fieldname": "description", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "products_section", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Products", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "Products to be shown on website homepage", + "fieldname": "products", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Products", + "length": 0, + "no_copy": 0, + "options": "Homepage Featured Product", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "40px" + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2016-04-22 05:57:38.701653", + "modified_by": "Administrator", + "module": "Portal", + "name": "Homepage", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Administrator", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "company", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py new file mode 100644 index 0000000000..6f55a59e62 --- /dev/null +++ b/erpnext/portal/doctype/homepage/homepage.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class Homepage(Document): + pass diff --git a/erpnext/portal/doctype/homepage_featured_product/__init__.py b/erpnext/portal/doctype/homepage_featured_product/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json new file mode 100644 index 0000000000..fe93098d12 --- /dev/null +++ b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.json @@ -0,0 +1,273 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "creation": "2016-04-22 05:57:06.261401", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "fields": [ + { + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "fieldname": "item_code", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_list_view": 1, + "label": "Item Code", + "length": 0, + "no_copy": 0, + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "150px", + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1, + "set_only_once": 0, + "unique": 0, + "width": "150px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "col_break1", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "item_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Item Name", + "length": 0, + "no_copy": 0, + "oldfieldname": "item_name", + "oldfieldtype": "Data", + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "print_width": "150", + "read_only": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "150" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 1, + "in_list_view": 1, + "label": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "description", + "oldfieldtype": "Small Text", + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "300px", + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "300px" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_7", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Image", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "thumbnail", + "fieldtype": "Attach Image", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Thumbnail", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "route", + "fieldtype": "Small Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "route", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-04-22 05:57:06.261401", + "modified_by": "Administrator", + "module": "Portal", + "name": "Homepage Featured Product", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py new file mode 100644 index 0000000000..936e07d34e --- /dev/null +++ b/erpnext/portal/doctype/homepage_featured_product/homepage_featured_product.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class HomepageFeaturedProduct(Document): + pass diff --git a/erpnext/portal/doctype/products_settings/__init__.py b/erpnext/portal/doctype/products_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/portal/doctype/products_settings/products_settings.js b/erpnext/portal/doctype/products_settings/products_settings.js new file mode 100644 index 0000000000..7a57abaf9b --- /dev/null +++ b/erpnext/portal/doctype/products_settings/products_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Products Settings', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/portal/doctype/products_settings/products_settings.json b/erpnext/portal/doctype/products_settings/products_settings.json new file mode 100644 index 0000000000..90de96c844 --- /dev/null +++ b/erpnext/portal/doctype/products_settings/products_settings.json @@ -0,0 +1,106 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2016-04-22 09:11:55.272398", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "description": "If checked, the Home page will be the default Item Group for the website", + "fieldname": "home_page_is_products", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Home Page is Products", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "products_as_list", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Show Products as a List", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2016-04-22 09:11:59.537639", + "modified_by": "Administrator", + "module": "Portal", + "name": "Products Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Website Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/portal/doctype/products_settings/products_settings.py b/erpnext/portal/doctype/products_settings/products_settings.py new file mode 100644 index 0000000000..f17ae9fee9 --- /dev/null +++ b/erpnext/portal/doctype/products_settings/products_settings.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint +from frappe.model.document import Document + +class ProductsSettings(Document): + def validate(self): + if self.home_page_is_products: + website_settings = frappe.get_doc('Website Settings') + website_settings.home_page = 'products' + website_settings.save() + +def home_page_is_products(doc, method): + '''Called on saving Website Settings''' + home_page_is_products = cint(frappe.db.get_single_value('Products Settings', 'home_page_is_products')) + if home_page_is_products: + doc.home_page = 'products' + diff --git a/erpnext/public/build.json b/erpnext/public/build.json index b3da719691..16e7488627 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -20,6 +20,7 @@ "public/js/pos/pos_tax_row.html", "public/js/pos/pos.js", "public/js/utils/item_selector.js", + "public/js/utils/inventory.js", "public/js/templates/item_selector.html" ] } diff --git a/erpnext/public/css/website.css b/erpnext/public/css/website.css index 8b455f3a08..8b5b900ce5 100644 --- a/erpnext/public/css/website.css +++ b/erpnext/public/css/website.css @@ -23,7 +23,7 @@ background-size: cover; background-repeat: no-repeat; background-position: center top; - border-bottom: 1px dashed #EBEFF2; + border-radius: 4px; } .product-image.missing-image { width: 100%; @@ -32,8 +32,9 @@ background-size: cover; background-repeat: no-repeat; background-position: center top; + border-radius: 4px; position: relative; - border-bottom: 1px dashed #EBEFF2; + background-color: #EBEFF2; } .product-image.missing-image .octicon { font-size: 32px; @@ -63,3 +64,63 @@ border-top: 2px solid #EBEFF2; padding-top: 10px; } +.featured-products { + border-top: 1px solid #EBEFF2; +} +.transaction-list-item { + border-bottom: none; + padding: 30px; + margin: 0px -30px; +} +.transaction-list-item:hover, +.transaction-list-item:active, +.transaction-list-item:focus { + background-color: #fafbfc; +} +.transaction-list-item .indicator { + font-size: inherit; + font-weight: inherit; + color: #8D99A6; + margin-left: -15px; +} +.transaction-list-item .items-preview, +.transaction-list-item .transaction-time { + margin-top: 5px; +} +.transaction-subheading .indicator { + font-weight: inherit; + color: #8D99A6; +} +.order-container { + margin: 50px 0px; +} +.order-container .order-item-header .h6 { + padding: 7px 15px; +} +.order-container .order-items { + margin: 30px 0px 0px; +} +.order-container .order-item-table { + margin: 0px -15px; +} +.order-container .order-item-header { + border-bottom: 1px solid #d1d8dd; +} +.order-container .order-image-col { + padding-right: 0px; +} +.order-container .order-image { + max-width: 55px; + max-height: 55px; + margin-top: -5px; +} +.order-container .order-taxes { + margin-top: 30px; +} +.order-container .order-taxes .row { + margin-top: 15px; +} +.order-container .tax-grand-total-row { + border-top: 1px solid #d1d8dd; + padding-top: 15px; +} diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index f08cf4279d..8bca282a2f 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -131,23 +131,4 @@ $(document).on('app_ready', function() { }); }); } -}); - -erpnext.get_item_dashboard_data = function(data, max_count) { - if(!max_count) max_count = 0; - data.forEach(function(d) { - d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production; - d.pending_qty = 0; - d.total_reserved = d.reserved_qty + d.reserved_qty_for_production; - if(d.actual_or_pending > d.actual_qty) { - d.pending_qty = d.actual_or_pending - d.actual_qty; - } - - max_count = Math.max(d.actual_or_pending, d.actual_qty, - d.total_reserved, max_count); - }); - return { - data: data, - max_count: max_count - } -} +}); \ No newline at end of file diff --git a/erpnext/public/js/utils/inventory.js b/erpnext/public/js/utils/inventory.js new file mode 100644 index 0000000000..80cd6a5268 --- /dev/null +++ b/erpnext/public/js/utils/inventory.js @@ -0,0 +1,77 @@ +erpnext.get_item_dashboard_data = function(data, max_count, show_item) { + if(!max_count) max_count = 0; + data.forEach(function(d) { + d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production; + d.pending_qty = 0; + d.total_reserved = d.reserved_qty + d.reserved_qty_for_production; + if(d.actual_or_pending > d.actual_qty) { + d.pending_qty = d.actual_or_pending - d.actual_qty; + } + + max_count = Math.max(d.actual_or_pending, d.actual_qty, + d.total_reserved, max_count); + }); + return { + data: data, + max_count: max_count, + show_item: show_item || false + } +} + +frappe.provide('erpnext.inventory'); + +erpnext.inventory.move_item = function(item, source, target, actual_qty, callback) { + var dialog = new frappe.ui.Dialog({ + title: target ? __('Add Item') : __('Move Item'), + fields: [ + {fieldname: 'item_code', label: __('Item'), + fieldtype: 'Link', options: 'Item', read_only: 1}, + {fieldname: 'source', label: __('Source Warehouse'), + fieldtype: 'Link', options: 'Warehouse', read_only: 1}, + {fieldname: 'target', label: __('Target Warehouse'), + fieldtype: 'Link', options: 'Warehouse', reqd: 1}, + {fieldname: 'qty', label: __('Quantity'), reqd: 1, + fieldtype: 'Float', description: __('Available {0}', [actual_qty]) }, + ], + }) + dialog.show(); + dialog.get_field('item_code').set_input(item); + + if(source) { + dialog.get_field('source').set_input(source); + } else { + dialog.get_field('source').df.hidden = 1; + dialog.get_field('source').refresh(); + } + + if(target) { + dialog.get_field('target').df.read_only = 1; + dialog.get_field('target').value = target; + dialog.get_field('target').refresh(); + } + + dialog.set_primary_action(__('Submit'), function() { + values = dialog.get_values(); + if(!values) { + return; + } + if(source && values.qty > actual_qty) { + frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty])); + return; + } + if(values.source === values.target) { + frappe.msgprint(__('Source and target warehouse must be different')); + } + + frappe.call({ + method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', + args: values, + callback: function(r) { + frappe.show_alert(__('Stock Entry {0} created', + ['' + r.message.name+ ''])); + dialog.hide(); + callback(r); + }, + }); + }); +} \ No newline at end of file diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less index 08ae03a767..b420ba6d8e 100644 --- a/erpnext/public/less/website.less +++ b/erpnext/public/less/website.less @@ -1,10 +1,13 @@ @border-color: #d1d8dd; -@light-border-color: #EBEFF2; +@light-border-color: #EBEFF2; +@text-muted: #8D99A6; +@light-bg: #fafbfc; .web-long-description { font-size: 18px; line-height: 200%; } + .item-stock { margin-bottom: 10px !important; } @@ -14,10 +17,6 @@ text-align: center; } -.product-image-wrapper { - -} - @media (max-width: 767px) { .product-image { height: 0px; @@ -33,13 +32,13 @@ background-size: cover; background-repeat: no-repeat; background-position: center top; - border-bottom: 1px dashed @light-border-color; + border-radius: 4px; } .product-image.missing-image { .product-image-square; position: relative; - border-bottom: 1px dashed @light-border-color; + background-color: @light-border-color; } .product-image.missing-image .octicon { @@ -74,3 +73,87 @@ border-top: 2px solid @light-border-color; padding-top:10px; } + +.featured-products { + border-top: 1px solid @light-border-color; +} + +.transaction-list-item { + border-bottom: none; + padding: 30px; + margin: 0px -30px; + + &:hover, + &:active, + &:focus { + background-color: @light-bg; + } + + .indicator { + font-size: inherit; + font-weight: inherit; + color: @text-muted; + margin-left: -15px; + } + + .transaction-time { + // margin-left: 15px; + } + + .items-preview, + .transaction-time { + margin-top: 5px; + } +} + +// order.html +.transaction-subheading { + .indicator { + font-weight: inherit; + color: @text-muted; + } +} + +.order-container { + margin: 50px 0px; + + .order-item-header .h6 { + padding: 7px 15px; + } + + .order-items { + margin: 30px 0px 0px; + } + + .order-item-table { + margin: 0px -15px; + } + + .order-item-header { + border-bottom: 1px solid #d1d8dd; + } + + .order-image-col { + padding-right: 0px; + } + + .order-image { + max-width: 55px; + max-height: 55px; + margin-top: -5px; + } + + .order-taxes { + margin-top: 30px; + + .row { + margin-top: 15px; + } + } + + .tax-grand-total-row { + border-top: 1px solid @border-color; + padding-top: 15px; + } +} + diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 878eb9b864..c8a8d7daa0 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -310,10 +310,13 @@ class SalesOrder(SellingController): def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) - list_context["show_sidebar"] = True - list_context["show_search"] = True - list_context["title"] = _("My Orders") - list_context["parents"] = [{"title": _("My Account"), "name": "me"}] + list_context.update({ + 'show_sidebar': True, + 'show_search': True, + 'no_breadcrumbs': True, + 'title': _('Orders'), + }) + return list_context @frappe.whitelist() @@ -338,10 +341,10 @@ def close_or_unclose_sales_orders(names, status): def make_material_request(source_name, target_doc=None): def postprocess(source, doc): doc.material_request_type = "Purchase" - + def update_item(source, target, source_parent): target.project = source_parent.project - + so = frappe.get_doc("Sales Order", source_name) diff --git a/erpnext/stock/doctype/item/item_dashboard.html b/erpnext/stock/doctype/item/item_dashboard.html index ac78e7935d..813aef33e2 100644 --- a/erpnext/stock/doctype/item/item_dashboard.html +++ b/erpnext/stock/doctype/item/item_dashboard.html @@ -1,11 +1,11 @@ {% for d in data %}
  • -
    + -
    - {% if d.item_code %} +
    + {% if show_item %} {{ d.item_code }} {% endif %} @@ -37,6 +37,18 @@
    +
    + {% if d.actual_qty %} +
  • {% endfor %} \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py index dcfc304655..c32c99b8e3 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py @@ -1,16 +1,34 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -import frappe +import frappe, erpnext +from frappe.utils import cint, flt +@frappe.whitelist() def make_stock_entry(**args): s = frappe.new_doc("Stock Entry") args = frappe._dict(args) + if args.posting_date: s.posting_date = args.posting_date if args.posting_time: s.posting_time = args.posting_time + # map names + if args.from_warehouse: + args.source = args.from_warehouse + if args.to_warehouse: + args.target = args.to_warehouse + if args.item_code: + args.item = args.item_code + + if isinstance(args.qty, basestring): + if '.' in args.qty: + args.qty = flt(args.qty) + else: + args.qty = cint(args.qty) + + # purpose if not args.purpose: if args.source and args.target: s.purpose = "Material Transfer" @@ -21,21 +39,34 @@ def make_stock_entry(**args): else: s.purpose = args.purpose - s.company = args.company or "_Test Company" + # company + if not args.company: + if args.source: + args.company = frappe.db.get_value('Warehouse', args.source, 'company') + elif args.target: + args.company = frappe.db.get_value('Warehouse', args.target, 'company') + + # set vales from test + if frappe.flags.in_test: + if not args.company: + args.company = '_Test Company' + if not args.item: + args.item = '_Test Item' + + s.company = args.company or erpnext.get_default_company() s.purchase_receipt_no = args.purchase_receipt_no s.delivery_note_no = args.delivery_note_no s.sales_invoice_no = args.sales_invoice_no - s.difference_account = args.difference_account or "Stock Adjustment - _TC" + if args.difference_account: + s.difference_account = args.difference_account s.append("items", { - "item_code": args.item or args.item_code or "_Test Item", - "s_warehouse": args.from_warehouse or args.source, - "t_warehouse": args.to_warehouse or args.target, + "item_code": args.item, + "s_warehouse": args.source, + "t_warehouse": args.target, "qty": args.qty, "basic_rate": args.basic_rate, - "expense_account": args.expense_account or "Stock Adjustment - _TC", "conversion_factor": 1.0, - "cost_center": "_Test Cost Center - _TC", "serial_no": args.serial_no }) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index d8c4382283..c42350a421 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -48,7 +48,7 @@ def get_item_details(args): if frappe.db.exists("Product Bundle", args.item_code): valuation_rate = 0.0 bundled_items = frappe.get_doc("Product Bundle", args.item_code) - + for bundle_item in bundled_items.items: valuation_rate += \ flt(get_valuation_rate(bundle_item.item_code, out.get("warehouse")).get("valuation_rate") \ @@ -83,7 +83,7 @@ def get_item_details(args): if args.get("is_subcontracted") == "Yes": out.bom = get_default_bom(args.item_code) - + get_gross_profit(out) return out @@ -101,7 +101,7 @@ def process_args(args): args.item_code = get_item_code(barcode=args.barcode) elif not args.item_code and args.serial_no: args.item_code = get_item_code(serial_no=args.serial_no) - + set_transaction_type(args) return args @@ -127,7 +127,7 @@ def validate_item_details(args, item): if args.transaction_type=="selling" and cint(item.has_variants): throw(_("Item {0} is a template, please select one of its variants").format(item.name)) - + elif args.transaction_type=="buying" and args.doctype != "Material Request": if args.get("is_subcontracted") == "Yes" and item.is_sub_contracted_item != 1: throw(_("Item {0} must be a Sub-contracted Item").format(item.name)) @@ -143,7 +143,7 @@ def get_basic_details(args, item): user_default_warehouse_list = get_user_default_as_list('Warehouse') user_default_warehouse = user_default_warehouse_list[0] \ if len(user_default_warehouse_list)==1 else "" - + warehouse = user_default_warehouse or args.warehouse or item.default_warehouse out = frappe._dict({ @@ -177,8 +177,8 @@ def get_basic_details(args, item): }) # if default specified in item is for another company, fetch from company - for d in [["Account", "income_account", "default_income_account"], - ["Account", "expense_account", "default_expense_account"], + for d in [["Account", "income_account", "default_income_account"], + ["Account", "expense_account", "default_expense_account"], ["Cost Center", "cost_center", "cost_center"], ["Warehouse", "warehouse", ""]]: company = frappe.db.get_value(d[0], out.get(d[1]), "company") if not out[d[1]] or (company and args.company != company): @@ -365,7 +365,7 @@ def get_projected_qty(item_code, warehouse): def get_bin_details(item_code, warehouse): return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty"], as_dict=True) \ - or {"projected_qty": 0, "actual_qty": 0, "valuation_rate": 0} + or {"projected_qty": 0, "actual_qty": 0} @frappe.whitelist() def get_batch_qty(batch_no,warehouse,item_code): @@ -473,31 +473,31 @@ def get_default_bom(item_code=None): return bom else: frappe.throw(_("No default BOM exists for Item {0}").format(item_code)) - + def get_valuation_rate(item_code, warehouse=None): item = frappe.get_doc("Item", item_code) if item.is_stock_item: if not warehouse: warehouse = item.default_warehouse - - return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, + + return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["valuation_rate"], as_dict=True) or {"valuation_rate": 0} - + elif not item.is_stock_item: - valuation_rate =frappe.db.sql("""select sum(base_net_amount) / sum(qty) - from `tabPurchase Invoice Item` + valuation_rate =frappe.db.sql("""select sum(base_net_amount) / sum(qty) + from `tabPurchase Invoice Item` where item_code = %s and docstatus=1""", item_code) - + if valuation_rate: return {"valuation_rate": valuation_rate[0][0] or 0.0} else: return {"valuation_rate": 0.0} - + def get_gross_profit(out): if out.valuation_rate: out.update({ "gross_profit": ((out.base_rate - out.valuation_rate) * out.qty) }) - + return out diff --git a/erpnext/stock/page/stock_balance/stock_balance.html b/erpnext/stock/page/stock_balance/stock_balance.html index a76252ea22..560f8436d3 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.html +++ b/erpnext/stock/page/stock_balance/stock_balance.html @@ -1,11 +1,5 @@
    -
    -
    -
    -
    -
    -