diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000000..452f3ec31fb
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,136 @@
+{
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ },
+ "extends": "eslint:recommended",
+ "rules": {
+ "indent": [
+ "error",
+ "tab",
+ { "SwitchCase": 1 }
+ ],
+ "linebreak-style": [
+ "error",
+ "unix"
+ ],
+ "quotes": [
+ "off"
+ ],
+ "semi": [
+ "warn",
+ "always"
+ ],
+ "camelcase": [
+ "off"
+ ],
+ "no-unused-vars": [
+ "warn"
+ ],
+ "no-redeclare": [
+ "warn"
+ ],
+ "no-console": [
+ "warn"
+ ],
+ "no-extra-boolean-cast": [
+ "off"
+ ],
+ "no-control-regex": [
+ "off"
+ ],
+ "spaced-comment": [
+ "warn"
+ ],
+ "no-trailing-spaces": [
+ "warn"
+ ]
+ },
+ "root": true,
+ "globals": {
+ "frappe": true,
+ "erpnext": true,
+ "schools": true,
+
+ "$": true,
+ "jQuery": true,
+ "moment": true,
+ "hljs": true,
+ "Awesomplete": true,
+ "CalHeatMap": true,
+ "Sortable": true,
+ "Showdown": true,
+ "Taggle": true,
+ "Gantt": true,
+ "Slick": true,
+ "PhotoSwipe": true,
+ "PhotoSwipeUI_Default": true,
+ "fluxify": true,
+ "io": true,
+ "c3": true,
+ "__": true,
+ "_p": true,
+ "_f": true,
+ "repl": true,
+ "Class": true,
+ "locals": true,
+ "cint": true,
+ "cstr": true,
+ "cur_frm": true,
+ "cur_dialog": true,
+ "cur_page": true,
+ "cur_list": true,
+ "cur_tree": true,
+ "msg_dialog": true,
+ "is_null": true,
+ "in_list": true,
+ "has_common": true,
+ "has_words": true,
+ "validate_email": true,
+ "get_number_format": true,
+ "format_number": true,
+ "format_currency": true,
+ "round_based_on_smallest_currency_fraction": true,
+ "roundNumber": true,
+ "comment_when": true,
+ "replace_newlines": true,
+ "open_url_post": true,
+ "toTitle": true,
+ "lstrip": true,
+ "strip": true,
+ "strip_html": true,
+ "replace_all": true,
+ "flt": true,
+ "precision": true,
+ "md5": true,
+ "CREATE": true,
+ "AMEND": true,
+ "CANCEL": true,
+ "copy_dict": true,
+ "get_number_format_info": true,
+ "print_table": true,
+ "Layout": true,
+ "web_form_settings": true,
+ "$c": true,
+ "$a": true,
+ "$i": true,
+ "$bg": true,
+ "$y": true,
+ "$c_obj": true,
+ "$c_obj_csv": true,
+ "refresh_many": true,
+ "refresh_field": true,
+ "toggle_field": true,
+ "get_field_obj": true,
+ "get_query_params": true,
+ "unhide_field": true,
+ "hide_field": true,
+ "set_field_options": true,
+ "getCookie": true,
+ "getCookies": true,
+ "get_url_arg": true,
+ "get_server_fields": true,
+ "set_multiple": true
+ }
+}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 273ca22a654..f27b2c879a5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -59,6 +59,7 @@ class PurchaseInvoice(BuyingController):
self.check_for_closed_status()
self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
self.set_expense_account(for_validate=True)
self.set_against_expense_account()
self.validate_write_off_account()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 17bfd576a5a..24da970db47 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -57,7 +57,8 @@ class SalesInvoice(SellingController):
self.so_dn_required()
self.validate_proj_cust()
self.validate_with_previous_doc()
- self.validate_uom_is_integer("stock_uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
+ self.validate_uom_is_integer("uom", "qty")
self.check_close_sales_order("sales_order")
self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index e7b0d1ac8d6..b96245e4936 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -43,7 +43,7 @@ class PurchaseOrder(BuyingController):
self.check_for_closed_status()
self.validate_uom_is_integer("uom", "qty")
- self.validate_uom_is_integer("stock_uom", ["qty", "required_qty"])
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_with_previous_doc()
self.validate_for_subcontracting()
diff --git a/erpnext/commands/__init__.py b/erpnext/commands/__init__.py
index 6ffa6a3e324..a991cf9881e 100644
--- a/erpnext/commands/__init__.py
+++ b/erpnext/commands/__init__.py
@@ -16,8 +16,11 @@ def call_command(cmd, context):
help='Run the demo for so many days. Default 100')
@click.option('--resume', default=False, is_flag=True,
help='Continue running the demo for given days')
+@click.option('--reinstall', default=False, is_flag=True,
+ help='Reinstall site before demo')
@pass_context
-def make_demo(context, site, domain='Manufacturing', days=100, resume=False):
+def make_demo(context, site, domain='Manufacturing', days=100,
+ resume=False, reinstall=False):
"Reinstall site and setup demo"
from frappe.commands.site import _reinstall
from frappe.installer import install_app
@@ -30,7 +33,8 @@ def make_demo(context, site, domain='Manufacturing', days=100, resume=False):
from erpnext.demo import demo
demo.simulate(days=days)
else:
- _reinstall(site, yes=True)
+ if reinstall:
+ _reinstall(site, yes=True)
with frappe.init_site(site=site):
frappe.connect()
if not 'erpnext' in frappe.get_installed_apps():
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3e35cdd4717..27a1f9f5429 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -73,10 +73,13 @@ class BuyingController(StockController):
def validate_stock_or_nonstock_items(self):
if self.meta.get_field("taxes") and not self.get_stock_items():
- tax_for_valuation = [d.account_head for d in self.get("taxes")
+ tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]]
+
if tax_for_valuation:
- frappe.throw(_("Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"))
+ for d in tax_for_valuation:
+ d.category = 'Total'
+ msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
def set_landed_cost_voucher_amount(self):
for d in self.get("items"):
diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py
index 3675f0f4ead..33f945f460b 100644
--- a/erpnext/demo/setup/setup_data.py
+++ b/erpnext/demo/setup/setup_data.py
@@ -116,7 +116,7 @@ def setup_user():
for u in json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'user.json')).read()):
user = frappe.new_doc("User")
user.update(u)
- user.flags.no_welcome_mail
+ user.flags.no_welcome_mail = True
user.new_password = 'demo'
user.insert()
diff --git a/erpnext/docs/assets/img/setup/email/email-actions.png b/erpnext/docs/assets/img/setup/email/email-actions.png
new file mode 100644
index 00000000000..eab88c646ad
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-actions.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-domain.png b/erpnext/docs/assets/img/setup/email/email-domain.png
new file mode 100644
index 00000000000..cae8fb2cea8
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-domain.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-folders.png b/erpnext/docs/assets/img/setup/email/email-folders.png
new file mode 100644
index 00000000000..68c823b0260
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-folders.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-inbox.png b/erpnext/docs/assets/img/setup/email/email-inbox.png
new file mode 100644
index 00000000000..486f76afc10
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-inbox.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-password.png b/erpnext/docs/assets/img/setup/email/email-password.png
new file mode 100644
index 00000000000..7c1edf7a10b
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-password.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-service.png b/erpnext/docs/assets/img/setup/email/email-service.png
new file mode 100644
index 00000000000..b63ff4e0ece
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-service.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-user-link.png b/erpnext/docs/assets/img/setup/email/email-user-link.png
new file mode 100644
index 00000000000..64f88d48fce
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-user-link.png differ
diff --git a/erpnext/docs/assets/img/setup/email/email-user.png b/erpnext/docs/assets/img/setup/email/email-user.png
new file mode 100644
index 00000000000..54b85a67c70
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/email-user.png differ
diff --git a/erpnext/docs/assets/img/setup/email/make-from-email.png b/erpnext/docs/assets/img/setup/email/make-from-email.png
new file mode 100644
index 00000000000..c7c35fb238f
Binary files /dev/null and b/erpnext/docs/assets/img/setup/email/make-from-email.png differ
diff --git a/erpnext/docs/user/manual/en/setting-up/email/email-inbox.md b/erpnext/docs/user/manual/en/setting-up/email/email-inbox.md
new file mode 100644
index 00000000000..d098d0c2243
--- /dev/null
+++ b/erpnext/docs/user/manual/en/setting-up/email/email-inbox.md
@@ -0,0 +1,86 @@
+# Email Inbox
+
+Business involves many transactional emails exchanges with parties like Customers and Suppliers, and within a company. Email Inbox feature allows you pull all your business emails into your ERPNext account. Accessing all the business emails with other transactions details makes ERPNext a single platform for accessing complete business information in one place.
+
+In ERPNext, you can configure Email Inbox for each System User. Following are the detailed steps to configure Email Inbox for a User.
+
+#### Step 1: Create User
+
+As mentioned above, you can configure Email Inbox for a System User only. Hence ensure that you have added yourself and your colleagues as a User and assigned them required permissions.
+
+To add new User, go to:
+
+`Setup > User > New User`
+
+
+
+#### Step 2: Create Email Domain
+
+To be able to send and receive emails into your ERPNext from other email service (like WebMail or Gmail), you should setup an Email Domain master. In this master, email gateway details like SMTP Address, Port No., IMAP/POP3 address details are captured. If you have ever configured a local email client (like Outlook), Email Domain master requires details to be fed in the similar way.
+
+To add new Email Domain, go to:
+
+`Setup > Emails > Email Domain > New`
+
+
+
+Once you have configured an Email Domain for your Email Service, it will be used for creating Email Accounts for all the Users in your ERPNext account.
+
+
+
+In the Email Account, select Email Domain only if you are using Email Service other than Email Services listed above. Else, you can just select Email Service, leave Email Domain blank and proceed forward.
+
+
+
+>If you are creating an Email Account for Email Inbox of a User, then leave Append To field as blank.
+
+For more details on how to setup Email Account, [click here]({{docs_base_url}}/user/manual/en/setting-up/email/email-account.html").
+
+#### Step 4: Linking Email Account in User master
+
+Once an Email Account is created for an User, select that Email Account in the User. This will ensure that emails pulled from the said Email ID will accessible only to this User in your ERPNext account.
+
+
+
+## Email Inbox
+
+If you have correctly configured Email Inbox as instructed above, then on the login of a User, Email Inbox icon will be visible. This will navigate user to Email Inbox view within the ERPNext account. All the Emails received on that email will be fetch and listed in the Email Inbox view. User will be able to open emails and take various actions against it.
+
+
+
+#### Folders
+
+In ERPNext, you can link multiple Email Accounts with the single User. To switch to Inbox of different email account and access other folders like Sent Emails, Spam, Trash, check Email Inbox option in the left bar.
+
+
+
+#### Actions
+
+On the emails in your inbox, you can take various actions like Reply, Forward, Mark as Spam or Trash.
+
+
+
+#### Make Options
+
+The Email Inbox within ERPNext also allow you to quickly create ERPNext transaction based on email received. From an Email itself, you can a Issue, Lead or Opportunity based on the context of the email.
+
+
+
+
diff --git a/erpnext/docs/user/manual/en/setting-up/email/index.txt b/erpnext/docs/user/manual/en/setting-up/email/index.txt
index 7ca1b942242..778fc812e37 100644
--- a/erpnext/docs/user/manual/en/setting-up/email/index.txt
+++ b/erpnext/docs/user/manual/en/setting-up/email/index.txt
@@ -1,4 +1,5 @@
email-account
+email-inbox
email-alerts
email-digest
email-reports
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index 7de305d85f6..be92074da10 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -14,6 +14,10 @@ def get_data():
'label': _('Payroll'),
'items': ['Salary Structure', 'Salary Slip', 'Timesheet']
},
+ {
+ 'label': _('Training Events/Results'),
+ 'items': ['Training Event', 'Training Result']
+ },
{
'label': _('Expense'),
'items': ['Expense Claim']
diff --git a/erpnext/hr/doctype/training_result/training_result.js b/erpnext/hr/doctype/training_result/training_result.js
index 3a3d70a27ab..62ac383ab78 100644
--- a/erpnext/hr/doctype/training_result/training_result.js
+++ b/erpnext/hr/doctype/training_result/training_result.js
@@ -3,13 +3,15 @@
frappe.ui.form.on('Training Result', {
onload: function(frm) {
- if (frm.doc.training_event) {
- frm.trigger("training_event");
- }
+ frm.trigger("training_event");
},
-
+
training_event: function(frm) {
- if (frm.doc.training_event) {
+ frm.trigger("training_event");
+ },
+
+ training_event: function(frm) {
+ if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) {
frappe.call({
method: "erpnext.hr.doctype.training_result.training_result.get_employees",
args: {
diff --git a/erpnext/hr/doctype/training_result/training_result.json b/erpnext/hr/doctype/training_result/training_result.json
index 52b9a1671c1..e5fbb5fd426 100644
--- a/erpnext/hr/doctype/training_result/training_result.json
+++ b/erpnext/hr/doctype/training_result/training_result.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "TRES.#####",
@@ -13,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -23,6 +25,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Training Event",
@@ -34,6 +37,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -41,6 +45,7 @@
"unique": 1
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -51,6 +56,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -60,6 +66,7 @@
"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,
@@ -67,6 +74,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -77,6 +85,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Employees",
@@ -88,13 +97,15 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -105,6 +116,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
+ "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
@@ -115,6 +127,7 @@
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -122,17 +135,17 @@
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-11-04 08:53:48.597031",
+ "modified": "2017-06-15 08:16:01.566531",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Result",
@@ -149,7 +162,6 @@
"export": 1,
"if_owner": 0,
"import": 0,
- "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -164,8 +176,10 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
+ "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "training_event",
+ "track_changes": 0,
"track_seen": 0
}
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6a80c961ffc..e8b43c806e6 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -404,3 +404,5 @@ erpnext.patches.v8_0.delete_bin_indexes
erpnext.patches.v8_0.move_account_head_from_account_to_warehouse_for_inventory
erpnext.patches.v8_0.change_in_words_varchar_length
erpnext.patches.v8_0.update_stock_qty_value_in_bom_item
+erpnext.patches.v8_0.create_domain_docs #16-05-2017
+
diff --git a/erpnext/patches/v8_0/create_domain_docs.py b/erpnext/patches/v8_0/create_domain_docs.py
new file mode 100644
index 00000000000..2f376db3d72
--- /dev/null
+++ b/erpnext/patches/v8_0/create_domain_docs.py
@@ -0,0 +1,52 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+import erpnext
+
+def execute():
+ """Create domain documents"""
+ frappe.reload_doc("core", "doctype", "domain")
+ frappe.reload_doc("core", "doctype", "domain_settings")
+ frappe.reload_doc("core", "doctype", "has_domain")
+
+ for domain in ("Distribution", "Manufacturing", "Retail", "Services", "Education"):
+ if not frappe.db.exists({"doctype": "Domain", "domain": domain}):
+ create_domain(domain)
+
+ # set domain in domain settings based on company domain
+
+ domains = []
+ condition = ""
+ company = erpnext.get_default_company()
+ if company:
+ condition = " and name='{0}'".format(company)
+
+ domains = frappe.db.sql_list("select distinct domain from `tabCompany` where domain != 'Other' {0}".format(condition))
+
+ if not domains:
+ return
+
+ domain_settings = frappe.get_doc("Domain Settings", "Domain Settings")
+ checked_domains = [row.domain for row in domain_settings.active_domains]
+
+ for domain in domains:
+ # check and ignore if the domains is already checked in domain settings
+ if domain in checked_domains:
+ continue
+
+ if not frappe.db.get_value("Domain", domain):
+ # user added custom domain in companies domain field
+ create_domain(domain)
+
+ row = domain_settings.append("active_domains", dict(domain=domain))
+
+ domain_settings.save(ignore_permissions=True)
+
+def create_domain(domain):
+ # create new domain
+
+ doc = frappe.new_doc("Domain")
+ doc.domain = domain
+ doc.db_update()
\ No newline at end of file
diff --git a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py b/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py
index 09fbd76523a..b59d81831f1 100644
--- a/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py
+++ b/erpnext/patches/v8_0/move_account_head_from_account_to_warehouse_for_inventory.py
@@ -12,4 +12,4 @@ def execute():
set
account = (select name from `tabAccount`
where account_type = 'Stock' and
- warehouse = `tabWarehouse`.name and is_group = 0)""")
\ No newline at end of file
+ warehouse = `tabWarehouse`.name and is_group = 0 limit 1)""")
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 0345f05861e..66df901bdca 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -47,7 +47,10 @@ class Project(Document):
self.append("tasks", task_map)
def get_tasks(self):
- return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
+ if self.name is None:
+ return {}
+ else:
+ return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc")
def validate(self):
self.validate_dates()
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 18260fb0117..2ba2aba863f 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -10,7 +10,8 @@ frappe.ui.form.on("Sales Order", {
'Delivery Note': 'Delivery',
'Sales Invoice': 'Invoice',
'Material Request': 'Material Request',
- 'Purchase Order': 'Purchase Order'
+ 'Purchase Order': 'Purchase Order',
+ 'Project': 'Project'
}
frm.add_fetch('customer', 'tax_id', 'tax_id');
},
@@ -120,6 +121,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
function() { me.make_maintenance_schedule() }, __("Make"));
}
+ // project
+ if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
+ this.frm.add_custom_button(__('Project'),
+ function() { me.make_project() }, __("Make"));
+ }
+
} else {
if (this.frm.has_perm("submit")) {
// un-close
@@ -264,6 +271,13 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
})
},
+ make_project: function() {
+ frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.sales_order.sales_order.make_project",
+ frm: this.frm
+ })
+ },
+
make_maintenance_visit: function() {
frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index cd75201c351..82950752262 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -32,7 +32,7 @@ class SalesOrder(SellingController):
self.validate_mandatory()
self.validate_proj_cust()
self.validate_po()
- self.validate_uom_is_integer("stock_uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
self.validate_for_items()
self.validate_warehouse()
@@ -408,6 +408,34 @@ def make_material_request(source_name, target_doc=None):
return doc
+@frappe.whitelist()
+def make_project(source_name, target_doc=None):
+ def postprocess(source, doc):
+ doc.project_type = "External"
+ doc.project_name = source.name
+
+ doc = get_mapped_doc("Sales Order", source_name, {
+ "Sales Order": {
+ "doctype": "Project",
+ "validation": {
+ "docstatus": ["=", 1]
+ },
+ "field_map":{
+ "name" : "sales_order",
+ "delivery_date" : "expected_end_date",
+ "base_grand_total" : "estimated_costing",
+ }
+ },
+ "Sales Order Item": {
+ "doctype": "Project Task",
+ "field_map": {
+ "description": "title",
+ },
+ }
+ }, target_doc, postprocess)
+
+ return doc
+
@frappe.whitelist()
def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):
diff --git a/erpnext/setup/setup_wizard/domainify.py b/erpnext/setup/setup_wizard/domainify.py
index 0069e886796..f0aaf029442 100644
--- a/erpnext/setup/setup_wizard/domainify.py
+++ b/erpnext/setup/setup_wizard/domainify.py
@@ -105,8 +105,9 @@ def setup_roles(data):
if data.allow_roles:
# remove all roles other than allowed roles
+ active_domains = frappe.get_active_domains()
data.allow_roles += ['Administrator', 'Guest', 'System Manager', 'All']
- for role in frappe.get_all('Role'):
+ for role in frappe.get_all('Role', filters = {"restrict_to_domain": ("not in", active_domains)}):
if not (role.name in data.allow_roles):
remove_role(role.name)
diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py
index ef276a034a1..43baf2f4fb2 100644
--- a/erpnext/setup/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/install_fixtures.py
@@ -13,6 +13,13 @@ default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
def install(country=None):
records = [
+ # domains
+ { 'doctype': 'Domain', 'domain': _('Distribution')},
+ { 'doctype': 'Domain', 'domain': _('Manufacturing')},
+ { 'doctype': 'Domain', 'domain': _('Retail')},
+ { 'doctype': 'Domain', 'domain': _('Services')},
+ { 'doctype': 'Domain', 'domain': _('Education')},
+
# address template
{'doctype':"Address Template", "country": country},
@@ -35,7 +42,7 @@ def install(country=None):
{'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'},
{'doctype': 'Salary Component', 'salary_component': _('Arrear'), 'description': _('Arrear'), 'type': 'Earning'},
{'doctype': 'Salary Component', 'salary_component': _('Leave Encashment'), 'description': _('Leave Encashment'), 'type': 'Earning'},
-
+
# expense claim type
{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
@@ -197,7 +204,7 @@ def install(country=None):
# Assessment Group
{'doctype': 'Assessment Group', 'assessment_group_name': _('All Assessment Groups'),
'is_group': 1, 'parent_assessment_group': ''},
-
+
]
from erpnext.setup.setup_wizard.industry_type import get_industry_types
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index 940d35c4821..5daa4e4d9e6 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -198,6 +198,10 @@ def set_defaults(args):
hr_settings.emp_created_by = "Naming Series"
hr_settings.save()
+ domain_settings = frappe.get_doc("Domain Settings")
+ domain_settings.append('active_domains', dict(domain=args.domain))
+ domain_settings.save()
+
def create_feed_and_todo():
"""update Activity feed and create todo for creation of item, customer, vendor"""
add_info_comment(**{
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 441b6370cc9..75234099507 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -102,7 +102,7 @@ class DeliveryNote(SellingController):
self.check_close_sales_order("against_sales_order")
self.validate_for_items()
self.validate_warehouse()
- self.validate_uom_is_integer("stock_uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
self.validate_with_previous_doc()
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 5b27a575b14..6e21e67d457 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -248,7 +248,7 @@ class Item(WebsiteGenerator):
self.set_attribute_context(context)
self.set_disabled_attributes(context)
- context.parents = self.get_parents(context)
+ self.get_parents(context)
return context